mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 10:35:30 +00:00
+personen
This commit is contained in:
@@ -52,6 +52,7 @@ func (p *AlmanachEditPage) GET(engine *templating.Engine, app core.App) HandleFu
|
|||||||
data["filters"] = filters
|
data["filters"] = filters
|
||||||
data["csrf_token"] = req.Session().Token
|
data["csrf_token"] = req.Session().Token
|
||||||
data["item_types"] = dbmodels.ITEM_TYPE_VALUES
|
data["item_types"] = dbmodels.ITEM_TYPE_VALUES
|
||||||
|
data["agent_relations"] = dbmodels.AGENT_RELATIONS
|
||||||
|
|
||||||
abbrs, err := pagemodels.GetAbks(app)
|
abbrs, err := pagemodels.GetAbks(app)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
99
controllers/api_agents.go
Normal file
99
controllers/api_agents.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/musenalm/app"
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels"
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/musenalm/middleware"
|
||||||
|
"github.com/Theodor-Springmann-Stiftung/musenalm/templating"
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
URL_API_AGENTS = "/api/agents"
|
||||||
|
URL_API_AGENTS_SEARCH = "/search"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app.Register(&AgentsAPI{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentsAPI struct{}
|
||||||
|
|
||||||
|
func (p *AgentsAPI) Up(app core.App, engine *templating.Engine) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentsAPI) Down(app core.App, engine *templating.Engine) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentsAPI) Setup(router *router.Router[*core.RequestEvent], app core.App, engine *templating.Engine) error {
|
||||||
|
rg := router.Group(URL_API_AGENTS)
|
||||||
|
rg.BindFunc(middleware.Authenticated(app))
|
||||||
|
rg.BindFunc(middleware.IsAdminOrEditor())
|
||||||
|
rg.GET(URL_API_AGENTS_SEARCH, p.searchHandler(app))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentsAPI) searchHandler(app core.App) HandleFunc {
|
||||||
|
return func(e *core.RequestEvent) error {
|
||||||
|
query := strings.TrimSpace(e.Request.URL.Query().Get("q"))
|
||||||
|
limit := parseAgentsLimit(e.Request.URL.Query().Get("limit"))
|
||||||
|
|
||||||
|
results, err := dbmodels.FTS5SearchAgents(app, query)
|
||||||
|
if err != nil {
|
||||||
|
primary, alt, err := dbmodels.BasicSearchAgents(app, query)
|
||||||
|
if err != nil {
|
||||||
|
app.Logger().Error("agent search failed", "query", query, "limit", limit, "error", err)
|
||||||
|
return e.JSON(http.StatusInternalServerError, map[string]any{
|
||||||
|
"error": "failed to search agents",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
results = append(primary, alt...)
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := map[string]bool{}
|
||||||
|
response := make([]map[string]string, 0, len(results))
|
||||||
|
for _, agent := range results {
|
||||||
|
if agent == nil || seen[agent.Id] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[agent.Id] = true
|
||||||
|
response = append(response, map[string]string{
|
||||||
|
"id": agent.Id,
|
||||||
|
"name": agent.Name(),
|
||||||
|
"detail": agent.Pseudonyms(),
|
||||||
|
"bio": agent.BiographicalData(),
|
||||||
|
})
|
||||||
|
if limit > 0 && len(response) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.JSON(http.StatusOK, map[string]any{
|
||||||
|
"agents": response,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAgentsLimit(value string) int {
|
||||||
|
if value == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
var gt = Object.defineProperty;
|
var At = Object.defineProperty;
|
||||||
var $ = (a) => {
|
var D = (a) => {
|
||||||
throw TypeError(a);
|
throw TypeError(a);
|
||||||
};
|
};
|
||||||
var bt = (a, i, t) => i in a ? gt(a, i, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[i] = t;
|
var Tt = (a, i, t) => i in a ? At(a, i, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[i] = t;
|
||||||
var p = (a, i, t) => bt(a, typeof i != "symbol" ? i + "" : i, t), y = (a, i, t) => i.has(a) || $("Cannot " + t);
|
var p = (a, i, t) => Tt(a, typeof i != "symbol" ? i + "" : i, t), C = (a, i, t) => i.has(a) || D("Cannot " + t);
|
||||||
var C = (a, i, t) => (y(a, i, "read from private field"), t ? t.call(a) : i.get(a)), _ = (a, i, t) => i.has(a) ? $("Cannot add the same private member more than once") : i instanceof WeakSet ? i.add(a) : i.set(a, t), b = (a, i, t, e) => (y(a, i, "write to private field"), e ? e.call(a, t) : i.set(a, t), t), E = (a, i, t) => (y(a, i, "access private method"), t);
|
var y = (a, i, t) => (C(a, i, "read from private field"), t ? t.call(a) : i.get(a)), _ = (a, i, t) => i.has(a) ? D("Cannot add the same private member more than once") : i instanceof WeakSet ? i.add(a) : i.set(a, t), b = (a, i, t, e) => (C(a, i, "write to private field"), e ? e.call(a, t) : i.set(a, t), t), E = (a, i, t) => (C(a, i, "access private method"), t);
|
||||||
class Et extends HTMLElement {
|
class It extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(), this._value = "", this.render();
|
super(), this._value = "", this.render();
|
||||||
}
|
}
|
||||||
@@ -74,9 +74,9 @@ class Et extends HTMLElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const S = "filter-list-list", St = "filter-list-item", Lt = "filter-list-input", N = "filter-list-searchable";
|
const S = "filter-list-list", Ct = "filter-list-item", yt = "filter-list-input", P = "filter-list-searchable";
|
||||||
var m, g, B;
|
var m, g, $;
|
||||||
class vt extends HTMLElement {
|
class wt extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
_(this, g);
|
_(this, g);
|
||||||
@@ -125,7 +125,7 @@ class vt extends HTMLElement {
|
|||||||
let t = this.querySelector("#" + S);
|
let t = this.querySelector("#" + S);
|
||||||
if (!t)
|
if (!t)
|
||||||
return;
|
return;
|
||||||
let e = new Mark(t.querySelectorAll("." + N));
|
let e = new Mark(t.querySelectorAll("." + P));
|
||||||
this._filter && e.mark(this._filter, {
|
this._filter && e.mark(this._filter, {
|
||||||
separateWordSearch: !0
|
separateWordSearch: !0
|
||||||
});
|
});
|
||||||
@@ -165,7 +165,7 @@ class vt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
getLinkText(t) {
|
getLinkText(t) {
|
||||||
let e = this.getSearchText(t);
|
let e = this.getSearchText(t);
|
||||||
return e === "" ? "" : `<span class="${N}">${e}</span>`;
|
return e === "" ? "" : `<span class="${P}">${e}</span>`;
|
||||||
}
|
}
|
||||||
getURL(t) {
|
getURL(t) {
|
||||||
if (this._queryparam) {
|
if (this._queryparam) {
|
||||||
@@ -187,7 +187,7 @@ class vt extends HTMLElement {
|
|||||||
`, htmx && htmx.process(this);
|
`, htmx && htmx.process(this);
|
||||||
}
|
}
|
||||||
ActiveDot(t) {
|
ActiveDot(t) {
|
||||||
return E(this, g, B).call(this, t), "";
|
return E(this, g, $).call(this, t), "";
|
||||||
}
|
}
|
||||||
NoItems(t) {
|
NoItems(t) {
|
||||||
return t.length === 0 ? '<div class="px-2 py-0.5 italic text-gray-500">Keine Einträge gefunden</div>' : "";
|
return t.length === 0 ? '<div class="px-2 py-0.5 italic text-gray-500">Keine Einträge gefunden</div>' : "";
|
||||||
@@ -200,7 +200,7 @@ class vt extends HTMLElement {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="${this._placeholder}"
|
placeholder="${this._placeholder}"
|
||||||
class="${Lt} w-full placeholder:italic px-2 py-0.5" />
|
class="${yt} w-full placeholder:italic px-2 py-0.5" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -215,7 +215,7 @@ class vt extends HTMLElement {
|
|||||||
t = this._items.filter((s) => e.every((n) => this.getSearchText(s).toLowerCase().includes(n.toLowerCase())));
|
t = this._items.filter((s) => e.every((n) => this.getSearchText(s).toLowerCase().includes(n.toLowerCase())));
|
||||||
}
|
}
|
||||||
return `
|
return `
|
||||||
<div id="${S}" class="${S} pt-1 max-h-60 overflow-auto bg-stone-50 ${C(this, m) ? "hidden" : ""}">
|
<div id="${S}" class="${S} pt-1 max-h-60 overflow-auto bg-stone-50 ${y(this, m) ? "hidden" : ""}">
|
||||||
${t.map(
|
${t.map(
|
||||||
(e, s) => `
|
(e, s) => `
|
||||||
<a
|
<a
|
||||||
@@ -224,8 +224,8 @@ class vt extends HTMLElement {
|
|||||||
hx-swap="outerHTML show:none"
|
hx-swap="outerHTML show:none"
|
||||||
hx-select="main"
|
hx-select="main"
|
||||||
hx-target="main"
|
hx-target="main"
|
||||||
class="${St} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${s % 2 === 0 ? "bg-stone-100" : "bg-stone-50"}"
|
class="${Ct} block px-2.5 py-0.5 hover:bg-slate-200 no-underline ${s % 2 === 0 ? "bg-stone-100" : "bg-stone-50"}"
|
||||||
${E(this, g, B).call(this, e) ? 'aria-current="page"' : ""}>
|
${E(this, g, $).call(this, e) ? 'aria-current="page"' : ""}>
|
||||||
${this.ActiveDot(e)}
|
${this.ActiveDot(e)}
|
||||||
${this.getLinkText(e)}
|
${this.getLinkText(e)}
|
||||||
</a>
|
</a>
|
||||||
@@ -236,13 +236,13 @@ class vt extends HTMLElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m = new WeakMap(), g = new WeakSet(), B = function(t) {
|
m = new WeakMap(), g = new WeakSet(), $ = function(t) {
|
||||||
if (!t)
|
if (!t)
|
||||||
return !1;
|
return !1;
|
||||||
let e = this.getHREF(t);
|
let e = this.getHREF(t);
|
||||||
return e === "" ? !1 : this._queryparam && (new URLSearchParams(window.location.search).get(this._queryparam) || "") === e ? !0 : !!window.location.href.endsWith(e);
|
return e === "" ? !1 : this._queryparam && (new URLSearchParams(window.location.search).get(this._queryparam) || "") === e ? !0 : !!window.location.href.endsWith(e);
|
||||||
};
|
};
|
||||||
class At extends HTMLElement {
|
class xt extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(), this.handleScroll = this.handleScroll.bind(this), this.scrollToTop = this.scrollToTop.bind(this);
|
super(), this.handleScroll = this.handleScroll.bind(this), this.scrollToTop = this.scrollToTop.bind(this);
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,7 @@ class At extends HTMLElement {
|
|||||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class Tt extends HTMLElement {
|
class Mt extends HTMLElement {
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
return ["position", "timeout"];
|
return ["position", "timeout"];
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,7 @@ class Tt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class It extends HTMLElement {
|
class Ot extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(), this.overlay = null, this._others = null, this._thisindex = -1, this._preview = null, this._description = null, this._imageURL = "", this._hideDLButton = !1;
|
super(), this.overlay = null, this._others = null, this._thisindex = -1, this._preview = null, this._description = null, this._imageURL = "", this._hideDLButton = !1;
|
||||||
}
|
}
|
||||||
@@ -487,7 +487,7 @@ class It extends HTMLElement {
|
|||||||
this.overlay.parentNode.removeChild(this.overlay), this.overlay = null;
|
this.overlay.parentNode.removeChild(this.overlay), this.overlay = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class yt extends HTMLElement {
|
class kt extends HTMLElement {
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
}
|
}
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -704,7 +704,7 @@ class f extends HTMLElement {
|
|||||||
return /\s|[.,;:!?]/.test(i);
|
return /\s|[.,;:!?]/.test(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class Ct extends HTMLElement {
|
class Rt extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -723,7 +723,7 @@ class Ct extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var A;
|
var A;
|
||||||
class wt extends HTMLElement {
|
class Bt extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
_(this, A, 176);
|
_(this, A, 176);
|
||||||
@@ -742,14 +742,14 @@ class wt extends HTMLElement {
|
|||||||
calculateShownImages() {
|
calculateShownImages() {
|
||||||
const t = this.getBoundingClientRect();
|
const t = this.getBoundingClientRect();
|
||||||
console.log(t);
|
console.log(t);
|
||||||
const e = Math.floor(t.width / (C(this, A) + 10));
|
const e = Math.floor(t.width / (y(this, A) + 10));
|
||||||
for (let s = 0; s < this._images.length; s++)
|
for (let s = 0; s < this._images.length; s++)
|
||||||
s < e - 1 ? this._images[s].classList.remove("hidden") : this._images[s].classList.add("hidden");
|
s < e - 1 ? this._images[s].classList.remove("hidden") : this._images[s].classList.add("hidden");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
A = new WeakMap();
|
A = new WeakMap();
|
||||||
const xt = "msr-component-wrapper", D = "msr-selected-items-container", P = "msr-placeholder-no-selection-text", Mt = "msr-selected-item-pill", Ot = "msr-selected-item-text", kt = "msr-item-name", Rt = "msr-item-additional-data", Bt = "msr-selected-item-role", H = "msr-selected-item-delete-btn", $t = "msr-controls-area", q = "msr-pre-add-button", U = "msr-input-area-wrapper", L = "msr-input-area-default-border", w = "msr-input-area-staged", F = "msr-staging-area-container", Nt = "msr-staged-item-pill", Dt = "msr-staged-item-text", x = "msr-staged-role-select", z = "msr-staged-cancel-btn", V = "msr-text-input", K = "msr-add-button", G = "msr-options-list", W = "msr-option-item", Pt = "msr-option-item-name", Ht = "msr-option-item-detail", j = "msr-option-item-highlighted", M = "msr-hidden-select", qt = "msr-state-no-selection", Ut = "msr-state-has-selection", Ft = "msr-state-list-open", zt = "msr-state-item-staged";
|
const Nt = "msr-component-wrapper", H = "msr-selected-items-container", q = "msr-placeholder-no-selection-text", $t = "msr-selected-item-pill", Dt = "msr-selected-item-text", Pt = "msr-item-name", Ht = "msr-item-additional-data", qt = "msr-selected-item-role", F = "msr-selected-item-delete-btn", Ft = "msr-controls-area", U = "msr-pre-add-button", K = "msr-input-area-wrapper", v = "msr-input-area-default-border", w = "msr-input-area-staged", z = "msr-staging-area-container", Ut = "msr-staged-item-pill", Kt = "msr-staged-item-text", x = "msr-staged-role-select", V = "msr-staged-cancel-btn", G = "msr-text-input", W = "msr-add-button", j = "msr-options-list", Q = "msr-option-item", zt = "msr-option-item-name", Vt = "msr-option-item-detail", J = "msr-option-item-highlighted", M = "msr-hidden-select", Gt = "msr-state-no-selection", Wt = "msr-state-has-selection", jt = "msr-state-list-open", Qt = "msr-state-item-staged";
|
||||||
class dt extends HTMLElement {
|
class ft extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
p(this, "_blurTimeout", null);
|
p(this, "_blurTimeout", null);
|
||||||
@@ -828,21 +828,21 @@ class dt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
_setupTemplates() {
|
_setupTemplates() {
|
||||||
this.optionTemplate = document.createElement("template"), this.optionTemplate.innerHTML = `
|
this.optionTemplate = document.createElement("template"), this.optionTemplate.innerHTML = `
|
||||||
<li role="option" class="${W} group">
|
<li role="option" class="${Q} group">
|
||||||
<span data-ref="nameEl" class="${Pt}"></span>
|
<span data-ref="nameEl" class="${zt}"></span>
|
||||||
<span data-ref="detailEl" class="${Ht}"></span>
|
<span data-ref="detailEl" class="${Vt}"></span>
|
||||||
</li>
|
</li>
|
||||||
`, this.selectedItemTemplate = document.createElement("template"), this.selectedItemTemplate.innerHTML = `
|
`, this.selectedItemTemplate = document.createElement("template"), this.selectedItemTemplate.innerHTML = `
|
||||||
<span class="${Mt} group">
|
<span class="${$t} group">
|
||||||
<span data-ref="textEl" class="${Ot}"></span>
|
<span data-ref="textEl" class="${Dt}"></span>
|
||||||
<button type="button" data-ref="deleteBtn" class="${H} ml-2">×</button>
|
<button type="button" data-ref="deleteBtn" class="${F} ml-2">×</button>
|
||||||
</span>
|
</span>
|
||||||
`, this.stagedPlacePillTemplate = document.createElement("template"), this.stagedPlacePillTemplate.innerHTML = `
|
`, this.stagedPlacePillTemplate = document.createElement("template"), this.stagedPlacePillTemplate.innerHTML = `
|
||||||
<span class="${Nt} flex items-center">
|
<span class="${Ut} flex items-center">
|
||||||
<span data-ref="nameEl" class="${Dt}"></span>
|
<span data-ref="nameEl" class="${Kt}"></span>
|
||||||
</span>
|
</span>
|
||||||
`, this.stagedCancelBtnTemplate = document.createElement("template"), this.stagedCancelBtnTemplate.innerHTML = `
|
`, this.stagedCancelBtnTemplate = document.createElement("template"), this.stagedCancelBtnTemplate.innerHTML = `
|
||||||
<button type="button" class="${z} flex items-center justify-center">×</button>
|
<button type="button" class="${V} flex items-center justify-center">×</button>
|
||||||
`, this.stagedRoleSelectTemplate = document.createElement("template"), this.stagedRoleSelectTemplate.innerHTML = `
|
`, this.stagedRoleSelectTemplate = document.createElement("template"), this.stagedRoleSelectTemplate.innerHTML = `
|
||||||
<select class="${x}">
|
<select class="${x}">
|
||||||
</select>
|
</select>
|
||||||
@@ -906,7 +906,7 @@ class dt extends HTMLElement {
|
|||||||
this.setAttribute("name", t), this.hiddenSelect && (this.hiddenSelect.name = t);
|
this.setAttribute("name", t), this.hiddenSelect && (this.hiddenSelect.name = t);
|
||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
if (this.placeholderNoSelection = this.getAttribute("placeholder-no-selection") || this._placeholderNoSelection, this.placeholderSearch = this.getAttribute("placeholder-search") || this._placeholderSearch, this.placeholderRoleSelect = this.getAttribute("placeholder-role-select") || this._placeholderRoleSelect, this._render(), this.inputAreaWrapper = this.querySelector(`.${U}`), this.inputElement = this.querySelector(`.${V}`), this.stagedItemPillContainer = this.querySelector(`.${F}`), this.optionsListElement = this.querySelector(`.${G}`), this.selectedItemsContainer = this.querySelector(`.${D}`), this.addButtonElement = this.querySelector(`.${K}`), this.preAddButtonElement = this.querySelector(`.${q}`), this.hiddenSelect = this.querySelector(`.${M}`), this.name && this.hiddenSelect && (this.hiddenSelect.name = this.name), this.hasAttribute("show-add-button") ? this.showAddButton = this.getAttribute("show-add-button") : this.setAttribute("show-add-button", String(this._showAddButton)), this.inputElement && (this.inputElement.placeholder = this.placeholderSearch), this.inputElement.addEventListener("input", this._handleInput), this.inputElement.addEventListener("keydown", this._handleInputKeyDown), this.inputElement.addEventListener("focus", this._handleFocus), this.inputElement.addEventListener("blur", this._handleBlur), this.optionsListElement.addEventListener("mousedown", this._handleOptionMouseDown), this.optionsListElement.addEventListener("click", this._handleOptionClick), this.addButtonElement.addEventListener("click", this._handleAddButtonClick), this.addEventListener("keydown", this._handleKeyDown), this._renderStagedPillOrInput(), this._updateAddButtonState(), this._updatePreAddButtonVisibility(), this._updateRootElementStateClasses(), this.hasAttribute("value")) {
|
if (this.placeholderNoSelection = this.getAttribute("placeholder-no-selection") || this._placeholderNoSelection, this.placeholderSearch = this.getAttribute("placeholder-search") || this._placeholderSearch, this.placeholderRoleSelect = this.getAttribute("placeholder-role-select") || this._placeholderRoleSelect, this._render(), this.inputAreaWrapper = this.querySelector(`.${K}`), this.inputElement = this.querySelector(`.${G}`), this.stagedItemPillContainer = this.querySelector(`.${z}`), this.optionsListElement = this.querySelector(`.${j}`), this.selectedItemsContainer = this.querySelector(`.${H}`), this.addButtonElement = this.querySelector(`.${W}`), this.preAddButtonElement = this.querySelector(`.${U}`), this.hiddenSelect = this.querySelector(`.${M}`), this.name && this.hiddenSelect && (this.hiddenSelect.name = this.name), this.hasAttribute("show-add-button") ? this.showAddButton = this.getAttribute("show-add-button") : this.setAttribute("show-add-button", String(this._showAddButton)), this.inputElement && (this.inputElement.placeholder = this.placeholderSearch), this.inputElement.addEventListener("input", this._handleInput), this.inputElement.addEventListener("keydown", this._handleInputKeyDown), this.inputElement.addEventListener("focus", this._handleFocus), this.inputElement.addEventListener("blur", this._handleBlur), this.optionsListElement.addEventListener("mousedown", this._handleOptionMouseDown), this.optionsListElement.addEventListener("click", this._handleOptionClick), this.addButtonElement.addEventListener("click", this._handleAddButtonClick), this.addEventListener("keydown", this._handleKeyDown), this._renderStagedPillOrInput(), this._updateAddButtonState(), this._updatePreAddButtonVisibility(), this._updateRootElementStateClasses(), this.hasAttribute("value")) {
|
||||||
const t = this.getAttribute("value");
|
const t = this.getAttribute("value");
|
||||||
try {
|
try {
|
||||||
const e = JSON.parse(t);
|
const e = JSON.parse(t);
|
||||||
@@ -934,7 +934,7 @@ class dt extends HTMLElement {
|
|||||||
this.disabledCallback(t);
|
this.disabledCallback(t);
|
||||||
}
|
}
|
||||||
disabledCallback(t) {
|
disabledCallback(t) {
|
||||||
this.inputElement && (this.inputElement.disabled = t), this.classList.toggle("pointer-events-none", t), this.querySelectorAll(`.${H}`).forEach(
|
this.inputElement && (this.inputElement.disabled = t), this.classList.toggle("pointer-events-none", t), this.querySelectorAll(`.${F}`).forEach(
|
||||||
(s) => s.disabled = t
|
(s) => s.disabled = t
|
||||||
);
|
);
|
||||||
const e = this.querySelector(`.${x}`);
|
const e = this.querySelector(`.${x}`);
|
||||||
@@ -957,7 +957,7 @@ class dt extends HTMLElement {
|
|||||||
this.internals_.setFormValue(null), this._synchronizeHiddenSelect();
|
this.internals_.setFormValue(null), this._synchronizeHiddenSelect();
|
||||||
}
|
}
|
||||||
_updateRootElementStateClasses() {
|
_updateRootElementStateClasses() {
|
||||||
this.classList.toggle(qt, this._value.length === 0), this.classList.toggle(Ut, this._value.length > 0), this.classList.toggle(Ft, this._isOptionsListVisible), this.classList.toggle(zt, !!this._stagedItem);
|
this.classList.toggle(Gt, this._value.length === 0), this.classList.toggle(Wt, this._value.length > 0), this.classList.toggle(jt, this._isOptionsListVisible), this.classList.toggle(Qt, !!this._stagedItem);
|
||||||
}
|
}
|
||||||
_render() {
|
_render() {
|
||||||
const t = this.id || `msr-${crypto.randomUUID().slice(0, 8)}`;
|
const t = this.id || `msr-${crypto.randomUUID().slice(0, 8)}`;
|
||||||
@@ -968,27 +968,27 @@ class dt extends HTMLElement {
|
|||||||
width: 0 !important; height: 0 !important; opacity: 0 !important; pointer-events: none !important;
|
width: 0 !important; height: 0 !important; opacity: 0 !important; pointer-events: none !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="${xt} relative">
|
<div class="${Nt} relative">
|
||||||
<div class="${D} flex flex-wrap gap-1 mb-2 min-h-[2.625rem] rounded-md" aria-live="polite">
|
<div class="${H} flex flex-wrap gap-1 mb-2 min-h-[2.625rem] rounded-md" aria-live="polite">
|
||||||
${this._value.length === 0 ? `<span class="${P}">${this.placeholderNoSelection}</span>` : ""}
|
${this._value.length === 0 ? `<span class="${q}">${this.placeholderNoSelection}</span>` : ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="${$t} flex items-center">
|
<div class="${Ft} flex items-center">
|
||||||
<div class="${U} ${L} flex-grow min-h-[42px] flex items-center flex-wrap gap-1" tabindex="-1">
|
<div class="${K} ${v} flex-grow min-h-[42px] flex items-center flex-wrap gap-1" tabindex="-1">
|
||||||
<span class="${F} flex items-center gap-2"></span>
|
<span class="${z} flex items-center gap-2"></span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="${V} flex-1 min-w-[100px] outline-none"
|
class="${G} flex-1 min-w-[100px] outline-none"
|
||||||
placeholder="${this.placeholderSearch}"
|
placeholder="${this.placeholderSearch}"
|
||||||
aria-haspopup="listbox"
|
aria-haspopup="listbox"
|
||||||
aria-expanded="false">
|
aria-expanded="false">
|
||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="${q} hidden flex items-center justify-center ml-2"
|
class="${U} hidden flex items-center justify-center ml-2"
|
||||||
aria-label="Element schnell hinzufügen">
|
aria-label="Element schnell hinzufügen">
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="${K} hidden ml-2">Hinzufügen</button>
|
<button type="button" class="${W} hidden ml-2">Hinzufügen</button>
|
||||||
</div>
|
</div>
|
||||||
<ul role="listbox" id="${t}-options-list" class="${G} absolute z-20 w-full max-h-60 overflow-y-auto mt-1 hidden"></ul>
|
<ul role="listbox" id="${t}-options-list" class="${j} absolute z-20 w-full max-h-60 overflow-y-auto mt-1 hidden"></ul>
|
||||||
<select multiple name="${this.getAttribute("name") || "items_with_roles_default"}" id="hidden-select-${t}" class="${M}" aria-hidden="true"></select>
|
<select multiple name="${this.getAttribute("name") || "items_with_roles_default"}" id="hidden-select-${t}" class="${M}" aria-hidden="true"></select>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -1011,7 +1011,7 @@ class dt extends HTMLElement {
|
|||||||
_renderStagedPillOrInput() {
|
_renderStagedPillOrInput() {
|
||||||
if (!(!this.stagedItemPillContainer || !this.inputElement || !this.inputAreaWrapper)) {
|
if (!(!this.stagedItemPillContainer || !this.inputElement || !this.inputAreaWrapper)) {
|
||||||
if (this.stagedItemPillContainer.innerHTML = "", this._stagedItem && this._stagedItem.item) {
|
if (this.stagedItemPillContainer.innerHTML = "", this._stagedItem && this._stagedItem.item) {
|
||||||
this.inputAreaWrapper.classList.remove(L), this.inputAreaWrapper.classList.add(w);
|
this.inputAreaWrapper.classList.remove(v), this.inputAreaWrapper.classList.add(w);
|
||||||
const t = this._createStagedItemPillElement(this._stagedItem.item);
|
const t = this._createStagedItemPillElement(this._stagedItem.item);
|
||||||
this.stagedItemPillContainer.appendChild(t);
|
this.stagedItemPillContainer.appendChild(t);
|
||||||
const e = this._getAvailableRolesForItem(this._stagedItem.item.id), s = this._createStagedRoleSelectElement(
|
const e = this._getAvailableRolesForItem(this._stagedItem.item.id), s = this._createStagedRoleSelectElement(
|
||||||
@@ -1022,7 +1022,7 @@ class dt extends HTMLElement {
|
|||||||
const n = this._createStagedCancelButtonElement(this._stagedItem.item.name);
|
const n = this._createStagedCancelButtonElement(this._stagedItem.item.name);
|
||||||
this.stagedItemPillContainer.appendChild(n), this.inputElement.classList.add("hidden"), this.inputElement.value = "", this.inputElement.removeAttribute("aria-activedescendant"), this.inputElement.setAttribute("aria-expanded", "false");
|
this.stagedItemPillContainer.appendChild(n), this.inputElement.classList.add("hidden"), this.inputElement.value = "", this.inputElement.removeAttribute("aria-activedescendant"), this.inputElement.setAttribute("aria-expanded", "false");
|
||||||
} else
|
} else
|
||||||
this.inputAreaWrapper.classList.add(L), this.inputAreaWrapper.classList.remove(w), this.inputElement.classList.remove("hidden");
|
this.inputAreaWrapper.classList.add(v), this.inputAreaWrapper.classList.remove(w), this.inputElement.classList.remove("hidden");
|
||||||
this._updateAddButtonState(), this._updatePreAddButtonVisibility(), this._updateRootElementStateClasses();
|
this._updateAddButtonState(), this._updatePreAddButtonVisibility(), this._updateRootElementStateClasses();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1041,15 +1041,15 @@ class dt extends HTMLElement {
|
|||||||
const e = this._getItemById(t.itemId);
|
const e = this._getItemById(t.itemId);
|
||||||
if (!e) return null;
|
if (!e) return null;
|
||||||
const n = this.selectedItemTemplate.content.cloneNode(!0).firstElementChild, r = n.querySelector('[data-ref="textEl"]');
|
const n = this.selectedItemTemplate.content.cloneNode(!0).firstElementChild, r = n.querySelector('[data-ref="textEl"]');
|
||||||
let l = `<span class="${kt}">${e.name}</span>`, o = e.additional_data ? ` <span class="${Rt}">(${e.additional_data})</span>` : "", h = ` <span class="${Bt}">${t.role}</span>`;
|
let l = `<span class="${Pt}">${e.name}</span>`, o = e.additional_data ? ` <span class="${Ht}">(${e.additional_data})</span>` : "", h = ` <span class="${qt}">${t.role}</span>`;
|
||||||
r.innerHTML = `${l}${o}${h}`;
|
r.innerHTML = `${l}${o}${h}`;
|
||||||
const u = n.querySelector('[data-ref="deleteBtn"]');
|
const u = n.querySelector('[data-ref="deleteBtn"]');
|
||||||
return u.setAttribute("aria-label", `Entferne ${e.name} als ${t.role}`), u.dataset.instanceId = t.instanceId, u.disabled = this.hasAttribute("disabled"), u.addEventListener("click", (ft) => {
|
return u.setAttribute("aria-label", `Entferne ${e.name} als ${t.role}`), u.dataset.instanceId = t.instanceId, u.disabled = this.hasAttribute("disabled"), u.addEventListener("click", (Lt) => {
|
||||||
ft.stopPropagation(), this._handleDeleteSelectedItem(t.instanceId);
|
Lt.stopPropagation(), this._handleDeleteSelectedItem(t.instanceId);
|
||||||
}), n;
|
}), n;
|
||||||
}
|
}
|
||||||
_renderSelectedItems() {
|
_renderSelectedItems() {
|
||||||
this.selectedItemsContainer && (this.selectedItemsContainer.innerHTML = "", this._value.length === 0 ? this.selectedItemsContainer.innerHTML = `<span class="${P}">${this.placeholderNoSelection}</span>` : this._value.forEach((t) => {
|
this.selectedItemsContainer && (this.selectedItemsContainer.innerHTML = "", this._value.length === 0 ? this.selectedItemsContainer.innerHTML = `<span class="${q}">${this.placeholderNoSelection}</span>` : this._value.forEach((t) => {
|
||||||
const e = this._createSelectedItemElement(t);
|
const e = this._createSelectedItemElement(t);
|
||||||
e && this.selectedItemsContainer.appendChild(e);
|
e && this.selectedItemsContainer.appendChild(e);
|
||||||
}), this._updateRootElementStateClasses());
|
}), this._updateRootElementStateClasses());
|
||||||
@@ -1062,7 +1062,7 @@ class dt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
_createOptionElement(t, e) {
|
_createOptionElement(t, e) {
|
||||||
const n = this.optionTemplate.content.cloneNode(!0).firstElementChild;
|
const n = this.optionTemplate.content.cloneNode(!0).firstElementChild;
|
||||||
return n.querySelector('[data-ref="nameEl"]').textContent = t.name, n.querySelector('[data-ref="detailEl"]').textContent = t.additional_data ? `(${t.additional_data})` : "", n.dataset.id = t.id, n.setAttribute("aria-selected", String(e === this._highlightedIndex)), n.id = `${this.id || "msr"}-option-${t.id}`, e === this._highlightedIndex && n.classList.add(j), n;
|
return n.querySelector('[data-ref="nameEl"]').textContent = t.name, n.querySelector('[data-ref="detailEl"]').textContent = t.additional_data ? `(${t.additional_data})` : "", n.dataset.id = t.id, n.setAttribute("aria-selected", String(e === this._highlightedIndex)), n.id = `${this.id || "msr"}-option-${t.id}`, e === this._highlightedIndex && n.classList.add(J), n;
|
||||||
}
|
}
|
||||||
_renderOptionsList() {
|
_renderOptionsList() {
|
||||||
if (!(!this.optionsListElement || !this.inputElement)) {
|
if (!(!this.optionsListElement || !this.inputElement)) {
|
||||||
@@ -1074,7 +1074,7 @@ class dt extends HTMLElement {
|
|||||||
this.optionsListElement.appendChild(n);
|
this.optionsListElement.appendChild(n);
|
||||||
});
|
});
|
||||||
const t = this.optionsListElement.querySelector(
|
const t = this.optionsListElement.querySelector(
|
||||||
`.${j}`
|
`.${J}`
|
||||||
);
|
);
|
||||||
t ? (t.scrollIntoView({ block: "nearest" }), this.inputElement.setAttribute("aria-activedescendant", t.id)) : this.inputElement.removeAttribute("aria-activedescendant");
|
t ? (t.scrollIntoView({ block: "nearest" }), this.inputElement.setAttribute("aria-activedescendant", t.id)) : this.inputElement.removeAttribute("aria-activedescendant");
|
||||||
}
|
}
|
||||||
@@ -1123,7 +1123,7 @@ class dt extends HTMLElement {
|
|||||||
if (!this.hasAttribute("disabled")) {
|
if (!this.hasAttribute("disabled")) {
|
||||||
if (t.key === "Enter" && this._stagedItem && this._stagedItem.item) {
|
if (t.key === "Enter" && this._stagedItem && this._stagedItem.item) {
|
||||||
const s = document.activeElement, n = (e = this.stagedItemPillContainer) == null ? void 0 : e.querySelector(
|
const s = document.activeElement, n = (e = this.stagedItemPillContainer) == null ? void 0 : e.querySelector(
|
||||||
`.${z}`
|
`.${V}`
|
||||||
);
|
);
|
||||||
if (s === n) {
|
if (s === n) {
|
||||||
t.preventDefault(), this._handleCancelStagedItem(t);
|
t.preventDefault(), this._handleCancelStagedItem(t);
|
||||||
@@ -1161,7 +1161,7 @@ class dt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
_handleFocus() {
|
_handleFocus() {
|
||||||
if (!(this.hasAttribute("disabled") || this.inputElement && this.inputElement.disabled || this._stagedItem)) {
|
if (!(this.hasAttribute("disabled") || this.inputElement && this.inputElement.disabled || this._stagedItem)) {
|
||||||
if (!this._stagedItem && this.inputAreaWrapper && (this.inputAreaWrapper.classList.add(L), this.inputAreaWrapper.classList.remove(w)), this.inputElement && this.inputElement.value.length > 0) {
|
if (!this._stagedItem && this.inputAreaWrapper && (this.inputAreaWrapper.classList.add(v), this.inputAreaWrapper.classList.remove(w)), this.inputElement && this.inputElement.value.length > 0) {
|
||||||
const t = this.inputElement.value.toLowerCase();
|
const t = this.inputElement.value.toLowerCase();
|
||||||
this._filteredOptions = this._options.filter((e) => this._getAvailableRolesForItem(e.id).length === 0 ? !1 : e.name.toLowerCase().includes(t) || e.additional_data && e.additional_data.toLowerCase().includes(t)), this._filteredOptions.length > 0 ? (this._isOptionsListVisible = !0, this._highlightedIndex = 0, this._renderOptionsList()) : this._hideOptionsList();
|
this._filteredOptions = this._options.filter((e) => this._getAvailableRolesForItem(e.id).length === 0 ? !1 : e.name.toLowerCase().includes(t) || e.additional_data && e.additional_data.toLowerCase().includes(t)), this._filteredOptions.length > 0 ? (this._isOptionsListVisible = !0, this._highlightedIndex = 0, this._renderOptionsList()) : this._hideOptionsList();
|
||||||
} else
|
} else
|
||||||
@@ -1180,7 +1180,7 @@ class dt extends HTMLElement {
|
|||||||
}
|
}
|
||||||
_handleOptionClick(t) {
|
_handleOptionClick(t) {
|
||||||
if (this.hasAttribute("disabled")) return;
|
if (this.hasAttribute("disabled")) return;
|
||||||
const e = t.target.closest(`li[data-id].${W}`);
|
const e = t.target.closest(`li[data-id].${Q}`);
|
||||||
if (e) {
|
if (e) {
|
||||||
const s = e.dataset.id, n = this._filteredOptions.find((r) => r.id === s);
|
const s = e.dataset.id, n = this._filteredOptions.find((r) => r.id === s);
|
||||||
n && this._stageItem(n);
|
n && this._stageItem(n);
|
||||||
@@ -1190,9 +1190,9 @@ class dt extends HTMLElement {
|
|||||||
this.hasAttribute("disabled") || (this._value = this._value.filter((e) => e.instanceId !== t), this._updateFormValue(), this._renderSelectedItems(), this._stagedItem && this._stagedItem.item && this._renderStagedPillOrInput(), this.inputElement && this.inputElement.focus(), this._updatePreAddButtonVisibility());
|
this.hasAttribute("disabled") || (this._value = this._value.filter((e) => e.instanceId !== t), this._updateFormValue(), this._renderSelectedItems(), this._stagedItem && this._stagedItem.item && this._renderStagedPillOrInput(), this.inputElement && this.inputElement.focus(), this._updatePreAddButtonVisibility());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p(dt, "formAssociated", !0);
|
p(ft, "formAssociated", !0);
|
||||||
const Vt = "mss-component-wrapper", Q = "mss-selected-items-container", Kt = "mss-selected-item-pill", Gt = "mss-selected-item-text", Wt = "mss-selected-item-pill-detail", J = "mss-selected-item-delete-btn", X = "mss-input-controls-container", Y = "mss-input-wrapper", Z = "mss-input-wrapper-focused", tt = "mss-text-input", et = "mss-create-new-button", it = "mss-options-list", jt = "mss-option-item", Qt = "mss-option-item-name", Jt = "mss-option-item-detail", st = "mss-option-item-highlighted", O = "mss-hidden-select", Xt = "mss-no-items-text", nt = "mss-loading", k = 1, R = 10, Yt = 250, Zt = "mss-state-no-selection", te = "mss-state-has-selection", ee = "mss-state-list-open";
|
const Jt = "mss-component-wrapper", X = "mss-selected-items-container", Xt = "mss-selected-item-pill", Yt = "mss-selected-item-text", Zt = "mss-selected-item-pill-detail", Y = "mss-selected-item-delete-btn", Z = "mss-input-controls-container", tt = "mss-input-wrapper", et = "mss-input-wrapper-focused", it = "mss-text-input", st = "mss-create-new-button", nt = "mss-options-list", te = "mss-option-item", ee = "mss-option-item-name", ie = "mss-option-item-detail", at = "mss-option-item-highlighted", O = "mss-hidden-select", se = "mss-no-items-text", rt = "mss-loading", k = 1, R = 10, ne = 250, ae = "mss-state-no-selection", re = "mss-state-has-selection", le = "mss-state-list-open";
|
||||||
class ct extends HTMLElement {
|
class gt extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
p(this, "_blurTimeout", null);
|
p(this, "_blurTimeout", null);
|
||||||
@@ -1384,15 +1384,15 @@ class ct extends HTMLElement {
|
|||||||
}
|
}
|
||||||
_setupTemplates() {
|
_setupTemplates() {
|
||||||
this.optionTemplate = document.createElement("template"), this.optionTemplate.innerHTML = `
|
this.optionTemplate = document.createElement("template"), this.optionTemplate.innerHTML = `
|
||||||
<li role="option" class="${jt}">
|
<li role="option" class="${te}">
|
||||||
<span data-ref="nameEl" class="${Qt}"></span>
|
<span data-ref="nameEl" class="${ee}"></span>
|
||||||
<span data-ref="detailEl" class="${Jt}"></span>
|
<span data-ref="detailEl" class="${ie}"></span>
|
||||||
</li>
|
</li>
|
||||||
`, this.selectedItemTemplate = document.createElement("template"), this.selectedItemTemplate.innerHTML = `
|
`, this.selectedItemTemplate = document.createElement("template"), this.selectedItemTemplate.innerHTML = `
|
||||||
<span class="${Kt} flex items-center">
|
<span class="${Xt} flex items-center">
|
||||||
<span data-ref="textEl" class="${Gt}"></span>
|
<span data-ref="textEl" class="${Yt}"></span>
|
||||||
<span data-ref="detailEl" class="${Wt} hidden"></span>
|
<span data-ref="detailEl" class="${Zt} hidden"></span>
|
||||||
<button type="button" data-ref="deleteBtn" class="${J}">×</button>
|
<button type="button" data-ref="deleteBtn" class="${Y}">×</button>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -1446,7 +1446,7 @@ class ct extends HTMLElement {
|
|||||||
this.setAttribute("name", t), this.hiddenSelect && (this.hiddenSelect.name = t);
|
this.setAttribute("name", t), this.hiddenSelect && (this.hiddenSelect.name = t);
|
||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
if (this._render(), this.inputControlsContainer = this.querySelector(`.${X}`), this.inputWrapper = this.querySelector(`.${Y}`), this.inputElement = this.querySelector(`.${tt}`), this.createNewButton = this.querySelector(`.${et}`), this.optionsListElement = this.querySelector(`.${it}`), this.selectedItemsContainer = this.querySelector(`.${Q}`), this.hiddenSelect = this.querySelector(`.${O}`), this.placeholder = this.getAttribute("placeholder") || "Search items...", this.showCreateButton = this.getAttribute("show-create-button") !== "false", this._remoteEndpoint = this.getAttribute("data-endpoint") || null, this._remoteResultKey = this.getAttribute("data-result-key") || "items", this._remoteMinChars = this._parsePositiveInt(this.getAttribute("data-minchars"), k), this._remoteLimit = this._parsePositiveInt(this.getAttribute("data-limit"), R), this.name && this.hiddenSelect && (this.hiddenSelect.name = this.name), this.inputElement.addEventListener("input", this._handleInput), this.inputElement.addEventListener("keydown", this._handleKeyDown), this.inputElement.addEventListener("focus", this._handleFocus), this.inputElement.addEventListener("blur", this._handleBlur), this.optionsListElement.addEventListener("mousedown", this._handleOptionMouseDown), this.optionsListElement.addEventListener("click", this._handleOptionClick), this.createNewButton.addEventListener("click", this._handleCreateNewButtonClick), this.selectedItemsContainer.addEventListener("click", this._handleSelectedItemsContainerClick), this._updateRootElementStateClasses(), this.hasAttribute("value")) {
|
if (this._render(), this.inputControlsContainer = this.querySelector(`.${Z}`), this.inputWrapper = this.querySelector(`.${tt}`), this.inputElement = this.querySelector(`.${it}`), this.createNewButton = this.querySelector(`.${st}`), this.optionsListElement = this.querySelector(`.${nt}`), this.selectedItemsContainer = this.querySelector(`.${X}`), this.hiddenSelect = this.querySelector(`.${O}`), this.placeholder = this.getAttribute("placeholder") || "Search items...", this.showCreateButton = this.getAttribute("show-create-button") !== "false", this._remoteEndpoint = this.getAttribute("data-endpoint") || null, this._remoteResultKey = this.getAttribute("data-result-key") || "items", this._remoteMinChars = this._parsePositiveInt(this.getAttribute("data-minchars"), k), this._remoteLimit = this._parsePositiveInt(this.getAttribute("data-limit"), R), this.name && this.hiddenSelect && (this.hiddenSelect.name = this.name), this.inputElement.addEventListener("input", this._handleInput), this.inputElement.addEventListener("keydown", this._handleKeyDown), this.inputElement.addEventListener("focus", this._handleFocus), this.inputElement.addEventListener("blur", this._handleBlur), this.optionsListElement.addEventListener("mousedown", this._handleOptionMouseDown), this.optionsListElement.addEventListener("click", this._handleOptionClick), this.createNewButton.addEventListener("click", this._handleCreateNewButtonClick), this.selectedItemsContainer.addEventListener("click", this._handleSelectedItemsContainerClick), this._updateRootElementStateClasses(), this.hasAttribute("value")) {
|
||||||
const t = this.getAttribute("value");
|
const t = this.getAttribute("value");
|
||||||
try {
|
try {
|
||||||
this.value = JSON.parse(t);
|
this.value = JSON.parse(t);
|
||||||
@@ -1498,10 +1498,10 @@ class ct extends HTMLElement {
|
|||||||
this.internals_.setFormValue(null), this._synchronizeHiddenSelect();
|
this.internals_.setFormValue(null), this._synchronizeHiddenSelect();
|
||||||
}
|
}
|
||||||
disabledCallback(t) {
|
disabledCallback(t) {
|
||||||
this.inputElement && (this.inputElement.disabled = t), this.createNewButton && (this.createNewButton.disabled = t), this.toggleAttribute("disabled", t), this.querySelectorAll(`.${J}`).forEach((e) => e.disabled = t), this.hiddenSelect && (this.hiddenSelect.disabled = t), t && this._hideOptionsList();
|
this.inputElement && (this.inputElement.disabled = t), this.createNewButton && (this.createNewButton.disabled = t), this.toggleAttribute("disabled", t), this.querySelectorAll(`.${Y}`).forEach((e) => e.disabled = t), this.hiddenSelect && (this.hiddenSelect.disabled = t), t && this._hideOptionsList();
|
||||||
}
|
}
|
||||||
_updateRootElementStateClasses() {
|
_updateRootElementStateClasses() {
|
||||||
this.classList.toggle(Zt, this._value.length === 0), this.classList.toggle(te, this._value.length > 0), this.classList.toggle(ee, this._isOptionsListVisible);
|
this.classList.toggle(ae, this._value.length === 0), this.classList.toggle(re, this._value.length > 0), this.classList.toggle(le, this._isOptionsListVisible);
|
||||||
}
|
}
|
||||||
_render() {
|
_render() {
|
||||||
const t = this.id || `mss-${crypto.randomUUID().slice(0, 8)}`;
|
const t = this.id || `mss-${crypto.randomUUID().slice(0, 8)}`;
|
||||||
@@ -1509,21 +1509,21 @@ class ct extends HTMLElement {
|
|||||||
<style>
|
<style>
|
||||||
.${O} { display: block !important; visibility: hidden !important; position: absolute !important; width: 0px !important; height: 0px !important; opacity: 0 !important; pointer-events: none !important; margin: -1px !important; padding: 0 !important; border: 0 !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; }
|
.${O} { display: block !important; visibility: hidden !important; position: absolute !important; width: 0px !important; height: 0px !important; opacity: 0 !important; pointer-events: none !important; margin: -1px !important; padding: 0 !important; border: 0 !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; }
|
||||||
</style>
|
</style>
|
||||||
<div class="${Vt} relative">
|
<div class="${Jt} relative">
|
||||||
<div class="${Q} flex flex-wrap gap-1 mb-1 min-h-[38px]" aria-live="polite" tabindex="-1"></div>
|
<div class="${X} flex flex-wrap gap-1 mb-1 min-h-[38px]" aria-live="polite" tabindex="-1"></div>
|
||||||
<div class="${X} flex items-center space-x-4">
|
<div class="${Z} flex items-center space-x-4">
|
||||||
<div class="${Y} relative rounded-md flex items-center flex-grow">
|
<div class="${tt} relative rounded-md flex items-center flex-grow">
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="${tt} w-full outline-none bg-transparent"
|
class="${it} w-full outline-none bg-transparent"
|
||||||
placeholder="${this.placeholder}"
|
placeholder="${this.placeholder}"
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-expanded="${this._isOptionsListVisible}"
|
aria-expanded="${this._isOptionsListVisible}"
|
||||||
aria-controls="options-list-${t}"
|
aria-controls="options-list-${t}"
|
||||||
autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="combobox" />
|
autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="combobox" />
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="${et} ${this.showCreateButton ? "" : "hidden"}" title="Create new item from input">+</button>
|
<button type="button" class="${st} ${this.showCreateButton ? "" : "hidden"}" title="Create new item from input">+</button>
|
||||||
</div>
|
</div>
|
||||||
<ul id="options-list-${t}" role="listbox" class="${it} absolute z-20 w-full max-h-60 overflow-y-auto mt-1 hidden"></ul>
|
<ul id="options-list-${t}" role="listbox" class="${nt} absolute z-20 w-full max-h-60 overflow-y-auto mt-1 hidden"></ul>
|
||||||
<select multiple name="${this.getAttribute("name") || "mss_default_name"}" id="hidden-select-${t}" class="${O}" aria-hidden="true"></select>
|
<select multiple name="${this.getAttribute("name") || "mss_default_name"}" id="hidden-select-${t}" class="${O}" aria-hidden="true"></select>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -1539,7 +1539,7 @@ class ct extends HTMLElement {
|
|||||||
}), n;
|
}), n;
|
||||||
}
|
}
|
||||||
_renderSelectedItems() {
|
_renderSelectedItems() {
|
||||||
this.selectedItemsContainer && (this.selectedItemsContainer.innerHTML = "", this._value.length === 0 ? this.selectedItemsContainer.innerHTML = `<span class="${Xt}">Keine Sprachen ausgewählt...</span>` : this._value.forEach((t) => {
|
this.selectedItemsContainer && (this.selectedItemsContainer.innerHTML = "", this._value.length === 0 ? this.selectedItemsContainer.innerHTML = `<span class="${se}">Keine Sprachen ausgewählt...</span>` : this._value.forEach((t) => {
|
||||||
const e = this._createSelectedItemElement(t);
|
const e = this._createSelectedItemElement(t);
|
||||||
e && this.selectedItemsContainer.appendChild(e);
|
e && this.selectedItemsContainer.appendChild(e);
|
||||||
}), this._updateRootElementStateClasses());
|
}), this._updateRootElementStateClasses());
|
||||||
@@ -1550,7 +1550,7 @@ class ct extends HTMLElement {
|
|||||||
const o = this._normalizeText(t.additional_data);
|
const o = this._normalizeText(t.additional_data);
|
||||||
l.textContent = o ? `(${o})` : "", n.dataset.id = t.id, n.setAttribute("aria-selected", String(e === this._highlightedIndex));
|
l.textContent = o ? `(${o})` : "", n.dataset.id = t.id, n.setAttribute("aria-selected", String(e === this._highlightedIndex));
|
||||||
const h = `option-${this.id || "mss"}-${t.id}`;
|
const h = `option-${this.id || "mss"}-${t.id}`;
|
||||||
return n.id = h, e === this._highlightedIndex && (n.classList.add(st), this.inputElement && this.inputElement.setAttribute("aria-activedescendant", h)), n;
|
return n.id = h, e === this._highlightedIndex && (n.classList.add(at), this.inputElement && this.inputElement.setAttribute("aria-activedescendant", h)), n;
|
||||||
}
|
}
|
||||||
_renderOptionsList() {
|
_renderOptionsList() {
|
||||||
if (!(!this.optionsListElement || !this.inputElement)) {
|
if (!(!this.optionsListElement || !this.inputElement)) {
|
||||||
@@ -1561,7 +1561,7 @@ class ct extends HTMLElement {
|
|||||||
const n = this._createOptionElement(e, s);
|
const n = this._createOptionElement(e, s);
|
||||||
this.optionsListElement.appendChild(n);
|
this.optionsListElement.appendChild(n);
|
||||||
});
|
});
|
||||||
const t = this.optionsListElement.querySelector(`.${st}`);
|
const t = this.optionsListElement.querySelector(`.${at}`);
|
||||||
t && (t.scrollIntoView({ block: "nearest" }), this.inputElement.setAttribute("aria-activedescendant", t.id));
|
t && (t.scrollIntoView({ block: "nearest" }), this.inputElement.setAttribute("aria-activedescendant", t.id));
|
||||||
}
|
}
|
||||||
this._updateRootElementStateClasses();
|
this._updateRootElementStateClasses();
|
||||||
@@ -1628,10 +1628,10 @@ class ct extends HTMLElement {
|
|||||||
this._isOptionsListVisible = !1, this._highlightedIndex = -1, this.optionsListElement && this._renderOptionsList();
|
this._isOptionsListVisible = !1, this._highlightedIndex = -1, this.optionsListElement && this._renderOptionsList();
|
||||||
}
|
}
|
||||||
_handleFocus() {
|
_handleFocus() {
|
||||||
this.inputElement.disabled || (this.inputWrapper && this.inputWrapper.classList.add(Z), this.inputElement.value.length > 0 && this._handleInput({ target: this.inputElement }), this._updateRootElementStateClasses());
|
this.inputElement.disabled || (this.inputWrapper && this.inputWrapper.classList.add(et), this.inputElement.value.length > 0 && this._handleInput({ target: this.inputElement }), this._updateRootElementStateClasses());
|
||||||
}
|
}
|
||||||
_handleBlur() {
|
_handleBlur() {
|
||||||
this.inputWrapper && this.inputWrapper.classList.remove(Z), this._blurTimeout = setTimeout(() => {
|
this.inputWrapper && this.inputWrapper.classList.remove(et), this._blurTimeout = setTimeout(() => {
|
||||||
this.contains(document.activeElement) || this._hideOptionsList();
|
this.contains(document.activeElement) || this._hideOptionsList();
|
||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
@@ -1660,14 +1660,14 @@ class ct extends HTMLElement {
|
|||||||
}
|
}
|
||||||
this._remoteFetchTimeout = setTimeout(() => {
|
this._remoteFetchTimeout = setTimeout(() => {
|
||||||
this._fetchRemoteOptions(t);
|
this._fetchRemoteOptions(t);
|
||||||
}, Yt);
|
}, ne);
|
||||||
}
|
}
|
||||||
_cancelRemoteFetch() {
|
_cancelRemoteFetch() {
|
||||||
this._remoteFetchController && (this._remoteFetchController.abort(), this._remoteFetchController = null);
|
this._remoteFetchController && (this._remoteFetchController.abort(), this._remoteFetchController = null);
|
||||||
}
|
}
|
||||||
async _fetchRemoteOptions(t) {
|
async _fetchRemoteOptions(t) {
|
||||||
if (!this._remoteEndpoint) return;
|
if (!this._remoteEndpoint) return;
|
||||||
this._cancelRemoteFetch(), this.classList.add(nt);
|
this._cancelRemoteFetch(), this.classList.add(rt);
|
||||||
const e = new AbortController();
|
const e = new AbortController();
|
||||||
this._remoteFetchController = e;
|
this._remoteFetchController = e;
|
||||||
try {
|
try {
|
||||||
@@ -1690,7 +1690,7 @@ class ct extends HTMLElement {
|
|||||||
return;
|
return;
|
||||||
console.error("MultiSelectSimple remote fetch error:", s), this._filteredOptions = [], this._isOptionsListVisible = !1, this._renderOptionsList();
|
console.error("MultiSelectSimple remote fetch error:", s), this._filteredOptions = [], this._isOptionsListVisible = !1, this._renderOptionsList();
|
||||||
} finally {
|
} finally {
|
||||||
this._remoteFetchController === e && (this._remoteFetchController = null), this.classList.remove(nt);
|
this._remoteFetchController === e && (this._remoteFetchController = null), this.classList.remove(rt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_extractRemoteOptions(t) {
|
_extractRemoteOptions(t) {
|
||||||
@@ -1724,9 +1724,9 @@ class ct extends HTMLElement {
|
|||||||
return (s === '"' && n === '"' || s === "'" && n === "'") && (e = e.slice(1, -1).trim(), !e) ? "" : e;
|
return (s === '"' && n === '"' || s === "'" && n === "'") && (e = e.slice(1, -1).trim(), !e) ? "" : e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p(ct, "formAssociated", !0);
|
p(gt, "formAssociated", !0);
|
||||||
const ie = "rbi-button", se = "rbi-icon";
|
const oe = "rbi-button", he = "rbi-icon";
|
||||||
class ne extends HTMLElement {
|
class de extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(), this.initialStates = /* @__PURE__ */ new Map(), this._controlledElements = [], this.button = null, this.lastOverallModifiedState = null, this.handleInputChange = this.handleInputChange.bind(this), this.handleReset = this.handleReset.bind(this);
|
super(), this.initialStates = /* @__PURE__ */ new Map(), this._controlledElements = [], this.button = null, this.lastOverallModifiedState = null, this.handleInputChange = this.handleInputChange.bind(this), this.handleReset = this.handleReset.bind(this);
|
||||||
}
|
}
|
||||||
@@ -1735,10 +1735,10 @@ class ne extends HTMLElement {
|
|||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
const i = `
|
const i = `
|
||||||
<button type="button" class="${ie} cursor-pointer disabled:cursor-default" aria-label="Reset field">
|
<button type="button" class="${oe} cursor-pointer disabled:cursor-default" aria-label="Reset field">
|
||||||
<tool-tip position="right">
|
<tool-tip position="right">
|
||||||
<div class="data-tip">Feld zurücksetzen</div>
|
<div class="data-tip">Feld zurücksetzen</div>
|
||||||
<span class="${se} ri-arrow-go-back-fill"></span>
|
<span class="${he} ri-arrow-go-back-fill"></span>
|
||||||
</tool-tip>
|
</tool-tip>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
@@ -1882,32 +1882,32 @@ class ne extends HTMLElement {
|
|||||||
this.button.setAttribute("aria-label", i);
|
this.button.setAttribute("aria-label", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const d = "hidden", at = "dm-stay", v = "dm-title", rt = "dm-menu-button", ae = "dm-target", re = "data-dm-target", lt = "dm-menu", ot = "dm-menu-item", le = "dm-close-button";
|
const d = "hidden", lt = "dm-stay", L = "dm-title", ot = "dm-menu-button", ce = "dm-target", ue = "data-dm-target", ht = "dm-menu", dt = "dm-menu-item", me = "dm-close-button";
|
||||||
var T, ut;
|
var T, bt;
|
||||||
class oe extends HTMLElement {
|
class pe extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
_(this, T);
|
_(this, T);
|
||||||
E(this, T, ut).call(this), this.boundHandleClickOutside = this.handleClickOutside.bind(this);
|
E(this, T, bt).call(this), this.boundHandleClickOutside = this.handleClickOutside.bind(this);
|
||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
if (this._target = document.getElementById(this.getAttribute(ae)), this._target || (this._target = this), this._cildren = Array.from(this.children).filter((t) => t.nodeType === Node.ELEMENT_NODE && !t.classList.contains(rt)).map((t) => ({
|
if (this._target = document.getElementById(this.getAttribute(ce)), this._target || (this._target = this), this._cildren = Array.from(this.children).filter((t) => t.nodeType === Node.ELEMENT_NODE && !t.classList.contains(ot)).map((t) => ({
|
||||||
node: t,
|
node: t,
|
||||||
target: () => {
|
target: () => {
|
||||||
const e = t.getAttribute(re);
|
const e = t.getAttribute(ue);
|
||||||
return e ? document.getElementById(e) || this._target : this._target;
|
return e ? document.getElementById(e) || this._target : this._target;
|
||||||
},
|
},
|
||||||
stay: () => t.hasAttribute(at) && t.getAttribute(at) == "true",
|
stay: () => t.hasAttribute(lt) && t.getAttribute(lt) == "true",
|
||||||
hidden: () => t.classList.contains(d),
|
hidden: () => t.classList.contains(d),
|
||||||
name: () => {
|
name: () => {
|
||||||
const e = t.querySelector("label");
|
const e = t.querySelector("label");
|
||||||
return e ? e.innerHTML : t.hasAttribute(v) ? t.getAttribute(v) : "";
|
return e ? e.innerHTML : t.hasAttribute(L) ? t.getAttribute(L) : "";
|
||||||
},
|
},
|
||||||
nameText: () => {
|
nameText: () => {
|
||||||
const e = t.querySelector("label");
|
const e = t.querySelector("label");
|
||||||
return e ? e.textContent.trim() : t.hasAttribute(v) ? t.getAttribute(v) : "";
|
return e ? e.textContent.trim() : t.hasAttribute(L) ? t.getAttribute(L) : "";
|
||||||
}
|
}
|
||||||
})), this._button = this.querySelector(`.${rt}`), !this._button) {
|
})), this._button = this.querySelector(`.${ot}`), !this._button) {
|
||||||
console.error("DivManagerMenu needs a button element.");
|
console.error("DivManagerMenu needs a button element.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1916,7 +1916,7 @@ class oe extends HTMLElement {
|
|||||||
this.removeChild(t.node);
|
this.removeChild(t.node);
|
||||||
this._button.addEventListener("click", this._toggleMenu.bind(this)), this._button.classList.add("relative");
|
this._button.addEventListener("click", this._toggleMenu.bind(this)), this._button.classList.add("relative");
|
||||||
for (const t of this._cildren)
|
for (const t of this._cildren)
|
||||||
t.node.querySelectorAll(`.${le}`).forEach((s) => {
|
t.node.querySelectorAll(`.${me}`).forEach((s) => {
|
||||||
s.addEventListener("click", (n) => {
|
s.addEventListener("click", (n) => {
|
||||||
this.hideDiv(n, t.node);
|
this.hideDiv(n, t.node);
|
||||||
});
|
});
|
||||||
@@ -1997,10 +1997,10 @@ ${t[0].nameText()} hinzufügen`, this._menu = null, this.hideMenu();
|
|||||||
this.hideMenu();
|
this.hideMenu();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(!this._menu || !this._button.contains(this._menu)) && (this._button.insertAdjacentHTML("beforeend", `<div class="${lt} absolute hidden"></div>`), this._menu = this._button.querySelector(`.${lt}`)), this._menu.innerHTML = `${t.map((s, n) => `
|
(!this._menu || !this._button.contains(this._menu)) && (this._button.insertAdjacentHTML("beforeend", `<div class="${ht} absolute hidden"></div>`), this._menu = this._button.querySelector(`.${ht}`)), this._menu.innerHTML = `${t.map((s, n) => `
|
||||||
<button type="button" class="${ot}" dm-itemno="${this._cildren.indexOf(s)}">
|
<button type="button" class="${dt}" dm-itemno="${this._cildren.indexOf(s)}">
|
||||||
${s.name()}
|
${s.name()}
|
||||||
</button>`).join("")}`, this._menu.querySelectorAll(`.${ot}`).forEach((s) => {
|
</button>`).join("")}`, this._menu.querySelectorAll(`.${dt}`).forEach((s) => {
|
||||||
s.addEventListener("click", (n) => {
|
s.addEventListener("click", (n) => {
|
||||||
this.showDiv(n, parseInt(s.getAttribute("dm-itemno"))), this.hideMenu(), this.renderButton();
|
this.showDiv(n, parseInt(s.getAttribute("dm-itemno"))), this.hideMenu(), this.renderButton();
|
||||||
});
|
});
|
||||||
@@ -2026,16 +2026,16 @@ ${t[0].nameText()} hinzufügen`, this._menu = null, this.hideMenu();
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
T = new WeakSet(), ut = function() {
|
T = new WeakSet(), bt = function() {
|
||||||
this._cildren = [], this._rendered = [], this._target = null, this._button = null, this._menu = null, this._originalButtonText = null;
|
this._cildren = [], this._rendered = [], this._target = null, this._button = null, this._menu = null, this._originalButtonText = null;
|
||||||
};
|
};
|
||||||
const c = "items-row", he = "items-list", de = "items-template", ce = "items-add-button", ue = "items-cancel-button", me = "items-remove-button", pe = "items-edit-button", _e = "items-close-button", fe = "items-summary", ge = "items-edit-panel", ht = "items_removed[]";
|
const c = "items-row", _e = "items-list", fe = "items-template", ge = "items-add-button", be = "items-cancel-button", Ee = "items-remove-button", Se = "items-edit-button", ve = "items-close-button", Le = "items-summary", Ae = "items-edit-panel", ct = "items_removed[]";
|
||||||
class be extends HTMLElement {
|
class Te extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(), this._list = null, this._template = null, this._addButton = null, this._idPrefix = `items-editor-${crypto.randomUUID().slice(0, 8)}`, this._handleAdd = this._onAddClick.bind(this);
|
super(), this._list = null, this._template = null, this._addButton = null, this._idPrefix = `items-editor-${crypto.randomUUID().slice(0, 8)}`, this._handleAdd = this._onAddClick.bind(this);
|
||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
if (this._list = this.querySelector(`.${he}`), this._template = this.querySelector(`template.${de}`), this._addButton = this.querySelector(`.${ce}`), !this._list || !this._template || !this._addButton) {
|
if (this._list = this.querySelector(`.${_e}`), this._template = this.querySelector(`template.${fe}`), this._addButton = this.querySelector(`.${ge}`), !this._list || !this._template || !this._addButton) {
|
||||||
console.error("ItemsEditor: Missing list, template, or add button.");
|
console.error("ItemsEditor: Missing list, template, or add button.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2063,14 +2063,14 @@ class be extends HTMLElement {
|
|||||||
s && this._ensureRemovalInput(s), t.remove(), this._refreshRowIds();
|
s && this._ensureRemovalInput(s), t.remove(), this._refreshRowIds();
|
||||||
}
|
}
|
||||||
_wireRemoveButtons(i = this) {
|
_wireRemoveButtons(i = this) {
|
||||||
i.querySelectorAll(`.${me}`).forEach((t) => {
|
i.querySelectorAll(`.${Ee}`).forEach((t) => {
|
||||||
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
||||||
e.preventDefault(), this.removeItem(t);
|
e.preventDefault(), this.removeItem(t);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_wireCancelButtons(i = this) {
|
_wireCancelButtons(i = this) {
|
||||||
i.querySelectorAll(`.${ue}`).forEach((t) => {
|
i.querySelectorAll(`.${be}`).forEach((t) => {
|
||||||
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const s = t.closest(`.${c}`);
|
const s = t.closest(`.${c}`);
|
||||||
@@ -2079,13 +2079,13 @@ class be extends HTMLElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
_wireEditButtons(i = this) {
|
_wireEditButtons(i = this) {
|
||||||
i.querySelectorAll(`.${pe}`).forEach((t) => {
|
i.querySelectorAll(`.${Se}`).forEach((t) => {
|
||||||
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const s = t.closest(`.${c}`);
|
const s = t.closest(`.${c}`);
|
||||||
s && this._setRowMode(s, "edit");
|
s && this._setRowMode(s, "edit");
|
||||||
}));
|
}));
|
||||||
}), i.querySelectorAll(`.${_e}`).forEach((t) => {
|
}), i.querySelectorAll(`.${ve}`).forEach((t) => {
|
||||||
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
t.dataset.itemsBound !== "true" && (t.dataset.itemsBound = "true", t.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const s = t.closest(`.${c}`);
|
const s = t.closest(`.${c}`);
|
||||||
@@ -2102,7 +2102,7 @@ class be extends HTMLElement {
|
|||||||
this._resetToOriginal(i), this._setRowMode(i, "summary");
|
this._resetToOriginal(i), this._setRowMode(i, "summary");
|
||||||
}
|
}
|
||||||
_setRowMode(i, t) {
|
_setRowMode(i, t) {
|
||||||
const e = i.querySelector(`.${fe}`), s = i.querySelector(`.${ge}`);
|
const e = i.querySelector(`.${Le}`), s = i.querySelector(`.${Ae}`);
|
||||||
!e || !s || (t === "edit" ? (e.classList.add("hidden"), s.classList.remove("hidden")) : (e.classList.remove("hidden"), s.classList.add("hidden"), this._syncSummary(i)));
|
!e || !s || (t === "edit" ? (e.classList.add("hidden"), s.classList.remove("hidden")) : (e.classList.remove("hidden"), s.classList.add("hidden"), this._syncSummary(i)));
|
||||||
}
|
}
|
||||||
_captureAllOriginals() {
|
_captureAllOriginals() {
|
||||||
@@ -2176,34 +2176,156 @@ class be extends HTMLElement {
|
|||||||
return i instanceof HTMLInputElement || i instanceof HTMLTextAreaElement ? i.value.trim() : "";
|
return i instanceof HTMLInputElement || i instanceof HTMLTextAreaElement ? i.value.trim() : "";
|
||||||
}
|
}
|
||||||
_ensureRemovalInput(i) {
|
_ensureRemovalInput(i) {
|
||||||
if (Array.from(this.querySelectorAll(`input[name="${ht}"]`)).some(
|
if (Array.from(this.querySelectorAll(`input[name="${ct}"]`)).some(
|
||||||
(s) => s.value === i
|
(s) => s.value === i
|
||||||
))
|
))
|
||||||
return;
|
return;
|
||||||
const e = document.createElement("input");
|
const e = document.createElement("input");
|
||||||
e.type = "hidden", e.name = ht, e.value = i, this.appendChild(e);
|
e.type = "hidden", e.name = ct, e.value = i, this.appendChild(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const Ee = "filter-list", Se = "scroll-button", Le = "tool-tip", ve = "abbrev-tooltips", Ae = "int-link", Te = "popup-image", Ie = "tab-list", ye = "filter-pill", Ce = "image-reel", we = "multi-select-places", xe = "multi-select-simple", mt = "reset-button", Me = "div-manager", Oe = "items-editor";
|
const Ie = "ssr-wrapper", ut = "ssr-input", mt = "ssr-list", Ce = "ssr-option", ye = "ssr-option-name", we = "ssr-option-detail", xe = "ssr-option-bio", pt = "ssr-hidden-input", _t = "ssr-clear-button", B = 1, N = 10, Me = 250;
|
||||||
customElements.define(Ae, Ct);
|
class Oe extends HTMLElement {
|
||||||
customElements.define(ve, f);
|
constructor() {
|
||||||
customElements.define(Ee, vt);
|
super(), this._endpoint = "", this._resultKey = "items", this._minChars = B, this._limit = N, this._placeholder = "Search...", this._options = [], this._selected = null, this._fetchTimeout = null, this._fetchController = null, this._listVisible = !1, this._boundHandleInput = this._handleInput.bind(this), this._boundHandleFocus = this._handleFocus.bind(this), this._boundHandleKeyDown = this._handleKeyDown.bind(this), this._boundHandleClear = this._handleClear.bind(this), this._boundHandleClickOutside = this._handleClickOutside.bind(this);
|
||||||
customElements.define(Se, At);
|
}
|
||||||
customElements.define(Le, Tt);
|
static get observedAttributes() {
|
||||||
customElements.define(Te, It);
|
return ["data-endpoint", "data-result-key", "data-minchars", "data-limit", "placeholder", "name"];
|
||||||
customElements.define(Ie, yt);
|
}
|
||||||
customElements.define(ye, Et);
|
connectedCallback() {
|
||||||
customElements.define(Ce, wt);
|
this._render(), this._input = this.querySelector(`.${ut}`), this._list = this.querySelector(`.${mt}`), this._hiddenInput = this.querySelector(`.${pt}`), this._clearButton = this.querySelector(`.${_t}`), this._endpoint = this.getAttribute("data-endpoint") || "", this._resultKey = this.getAttribute("data-result-key") || "items", this._minChars = this._parsePositiveInt(this.getAttribute("data-minchars"), B), this._limit = this._parsePositiveInt(this.getAttribute("data-limit"), N), this._placeholder = this.getAttribute("placeholder") || "Search...", this._input && (this._input.placeholder = this._placeholder, this._input.addEventListener("input", this._boundHandleInput), this._input.addEventListener("focus", this._boundHandleFocus), this._input.addEventListener("keydown", this._boundHandleKeyDown)), this._clearButton && this._clearButton.addEventListener("click", this._boundHandleClear), document.addEventListener("click", this._boundHandleClickOutside);
|
||||||
customElements.define(we, dt);
|
}
|
||||||
customElements.define(xe, ct);
|
disconnectedCallback() {
|
||||||
customElements.define(mt, ne);
|
document.removeEventListener("click", this._boundHandleClickOutside), this._input && (this._input.removeEventListener("input", this._boundHandleInput), this._input.removeEventListener("focus", this._boundHandleFocus), this._input.removeEventListener("keydown", this._boundHandleKeyDown)), this._clearButton && this._clearButton.removeEventListener("click", this._boundHandleClear);
|
||||||
customElements.define(Me, oe);
|
}
|
||||||
customElements.define(Oe, be);
|
attributeChangedCallback(i, t, e) {
|
||||||
function ke() {
|
t !== e && (i === "data-endpoint" && (this._endpoint = e || ""), i === "data-result-key" && (this._resultKey = e || "items"), i === "data-minchars" && (this._minChars = this._parsePositiveInt(e, B)), i === "data-limit" && (this._limit = this._parsePositiveInt(e, N)), i === "placeholder" && (this._placeholder = e || "Search...", this._input && (this._input.placeholder = this._placeholder)), i === "name" && this._hiddenInput && (this._hiddenInput.name = e || ""));
|
||||||
|
}
|
||||||
|
_handleInput(i) {
|
||||||
|
const t = i.target.value.trim();
|
||||||
|
if (this._selected = null, this._syncHiddenInput(), t.length < this._minChars) {
|
||||||
|
this._options = [], this._renderOptions(), this._hideList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._debouncedFetch(t);
|
||||||
|
}
|
||||||
|
_handleFocus() {
|
||||||
|
this._options.length > 0 && this._showList();
|
||||||
|
}
|
||||||
|
_handleKeyDown(i) {
|
||||||
|
i.key === "Escape" && this._hideList();
|
||||||
|
}
|
||||||
|
_handleClear(i) {
|
||||||
|
i.preventDefault(), this._selected = null, this._options = [], this._input && (this._input.value = ""), this._syncHiddenInput(), this._renderOptions(), this._hideList(), this.dispatchEvent(new CustomEvent("ssrchange", { bubbles: !0, detail: { item: null } }));
|
||||||
|
}
|
||||||
|
_handleClickOutside(i) {
|
||||||
|
this.contains(i.target) || this._hideList();
|
||||||
|
}
|
||||||
|
_debouncedFetch(i) {
|
||||||
|
this._fetchTimeout && clearTimeout(this._fetchTimeout), this._fetchTimeout = setTimeout(() => {
|
||||||
|
this._fetchOptions(i);
|
||||||
|
}, Me);
|
||||||
|
}
|
||||||
|
async _fetchOptions(i) {
|
||||||
|
if (!this._endpoint)
|
||||||
|
return;
|
||||||
|
this._fetchController && this._fetchController.abort(), this._fetchController = new AbortController();
|
||||||
|
const t = new URL(this._endpoint, window.location.origin);
|
||||||
|
t.searchParams.set("q", i), this._limit > 0 && t.searchParams.set("limit", String(this._limit));
|
||||||
|
try {
|
||||||
|
const e = await fetch(t.toString(), { signal: this._fetchController.signal });
|
||||||
|
if (!e.ok)
|
||||||
|
return;
|
||||||
|
const s = await e.json(), n = Array.isArray(s == null ? void 0 : s[this._resultKey]) ? s[this._resultKey] : [];
|
||||||
|
this._options = n.filter((r) => r && r.id && r.name), this._renderOptions(), this._options.length > 0 ? this._showList() : this._hideList();
|
||||||
|
} catch (e) {
|
||||||
|
if ((e == null ? void 0 : e.name) === "AbortError")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_renderOptions() {
|
||||||
|
this._list && (this._list.innerHTML = "", this._options.forEach((i) => {
|
||||||
|
const t = document.createElement("button");
|
||||||
|
t.type = "button", t.className = [
|
||||||
|
Ce,
|
||||||
|
"w-full text-left px-3 py-2 hover:bg-slate-100 transition-colors"
|
||||||
|
].join(" ");
|
||||||
|
const e = document.createElement("div");
|
||||||
|
if (e.className = [ye, "text-sm font-semibold text-gray-800"].join(" "), e.textContent = i.name, t.appendChild(e), i.detail) {
|
||||||
|
const s = document.createElement("div");
|
||||||
|
s.className = [we, "text-xs text-gray-600"].join(" "), s.textContent = i.detail, t.appendChild(s);
|
||||||
|
}
|
||||||
|
if (i.bio) {
|
||||||
|
const s = document.createElement("div");
|
||||||
|
s.className = [xe, "text-xs text-gray-500"].join(" "), s.textContent = i.bio, t.appendChild(s);
|
||||||
|
}
|
||||||
|
t.addEventListener("click", () => {
|
||||||
|
this._selectOption(i);
|
||||||
|
}), this._list.appendChild(t);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
_selectOption(i) {
|
||||||
|
this._selected = i, this._input && (this._input.value = i.name || ""), this._syncHiddenInput(), this._hideList(), this.dispatchEvent(new CustomEvent("ssrchange", { bubbles: !0, detail: { item: i } })), this.dispatchEvent(new Event("change", { bubbles: !0 }));
|
||||||
|
}
|
||||||
|
_syncHiddenInput() {
|
||||||
|
var i;
|
||||||
|
this._hiddenInput && (this._hiddenInput.value = ((i = this._selected) == null ? void 0 : i.id) || "");
|
||||||
|
}
|
||||||
|
_showList() {
|
||||||
|
!this._list || this._listVisible || (this._list.classList.remove("hidden"), this._listVisible = !0);
|
||||||
|
}
|
||||||
|
_hideList() {
|
||||||
|
!this._list || !this._listVisible || (this._list.classList.add("hidden"), this._listVisible = !1);
|
||||||
|
}
|
||||||
|
_parsePositiveInt(i, t) {
|
||||||
|
const e = parseInt(i || "", 10);
|
||||||
|
return Number.isNaN(e) || e <= 0 ? t : e;
|
||||||
|
}
|
||||||
|
_render() {
|
||||||
|
const i = this.getAttribute("name") || "";
|
||||||
|
this.innerHTML = `
|
||||||
|
<div class="${Ie} relative">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="${ut} inputinput w-full"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
autocapitalize="none"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder="${this._placeholder}"
|
||||||
|
/>
|
||||||
|
<button type="button" class="${_t} text-sm text-gray-600 hover:text-gray-900">
|
||||||
|
<i class="ri-close-line"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" class="${pt}" name="${i}" value="" />
|
||||||
|
<div class="${mt} absolute left-0 right-0 mt-1 border border-stone-200 rounded-xs bg-white shadow-sm z-10 hidden max-h-64 overflow-auto"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ke = "filter-list", Re = "scroll-button", Be = "tool-tip", Ne = "abbrev-tooltips", $e = "int-link", De = "popup-image", Pe = "tab-list", He = "filter-pill", qe = "image-reel", Fe = "multi-select-places", Ue = "multi-select-simple", Ke = "single-select-remote", Et = "reset-button", ze = "div-manager", Ve = "items-editor";
|
||||||
|
customElements.define($e, Rt);
|
||||||
|
customElements.define(Ne, f);
|
||||||
|
customElements.define(ke, wt);
|
||||||
|
customElements.define(Re, xt);
|
||||||
|
customElements.define(Be, Mt);
|
||||||
|
customElements.define(De, Ot);
|
||||||
|
customElements.define(Pe, kt);
|
||||||
|
customElements.define(He, It);
|
||||||
|
customElements.define(qe, Bt);
|
||||||
|
customElements.define(Fe, ft);
|
||||||
|
customElements.define(Ue, gt);
|
||||||
|
customElements.define(Ke, Oe);
|
||||||
|
customElements.define(Et, de);
|
||||||
|
customElements.define(ze, pe);
|
||||||
|
customElements.define(Ve, Te);
|
||||||
|
function Ge() {
|
||||||
const a = window.location.pathname, i = window.location.search, t = a + i;
|
const a = window.location.pathname, i = window.location.search, t = a + i;
|
||||||
return encodeURIComponent(t);
|
return encodeURIComponent(t);
|
||||||
}
|
}
|
||||||
function Re(a = 5e3, i = 100) {
|
function We(a = 5e3, i = 100) {
|
||||||
return new Promise((t, e) => {
|
return new Promise((t, e) => {
|
||||||
let s = 0;
|
let s = 0;
|
||||||
const n = setInterval(() => {
|
const n = setInterval(() => {
|
||||||
@@ -2211,8 +2333,8 @@ function Re(a = 5e3, i = 100) {
|
|||||||
}, i);
|
}, i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
async function Be(a) {
|
async function je(a) {
|
||||||
const i = await Re(), t = document.getElementById("qr");
|
const i = await We(), t = document.getElementById("qr");
|
||||||
t && (t.innerHTML = "", t.classList.add("hidden"), new i(t, {
|
t && (t.innerHTML = "", t.classList.add("hidden"), new i(t, {
|
||||||
text: a,
|
text: a,
|
||||||
width: 1280,
|
width: 1280,
|
||||||
@@ -2224,7 +2346,7 @@ async function Be(a) {
|
|||||||
t.classList.remove("hidden");
|
t.classList.remove("hidden");
|
||||||
}, 20));
|
}, 20));
|
||||||
}
|
}
|
||||||
function $e(a) {
|
function Qe(a) {
|
||||||
a && (a.addEventListener("focus", (i) => {
|
a && (a.addEventListener("focus", (i) => {
|
||||||
i.preventDefault(), a.select();
|
i.preventDefault(), a.select();
|
||||||
}), a.addEventListener("mousedown", (i) => {
|
}), a.addEventListener("mousedown", (i) => {
|
||||||
@@ -2237,7 +2359,7 @@ function $e(a) {
|
|||||||
a.select();
|
a.select();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
function Ne() {
|
function Je() {
|
||||||
document.body.addEventListener("htmx:responseError", function(a) {
|
document.body.addEventListener("htmx:responseError", function(a) {
|
||||||
const i = a.detail.requestConfig;
|
const i = a.detail.requestConfig;
|
||||||
if (i.boosted) {
|
if (i.boosted) {
|
||||||
@@ -2247,7 +2369,7 @@ function Ne() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function De(a, i) {
|
function Xe(a, i) {
|
||||||
if (!(a instanceof HTMLElement)) {
|
if (!(a instanceof HTMLElement)) {
|
||||||
console.warn("Target must be an HTMLElement.");
|
console.warn("Target must be an HTMLElement.");
|
||||||
return;
|
return;
|
||||||
@@ -2256,7 +2378,7 @@ function De(a, i) {
|
|||||||
console.warn("Action must be a function.");
|
console.warn("Action must be a function.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const t = a.querySelectorAll(mt);
|
const t = a.querySelectorAll(Et);
|
||||||
a.addEventListener("rbichange", (e) => {
|
a.addEventListener("rbichange", (e) => {
|
||||||
for (const s of t)
|
for (const s of t)
|
||||||
if (s.isCurrentlyModified()) {
|
if (s.isCurrentlyModified()) {
|
||||||
@@ -2273,10 +2395,10 @@ function I(a) {
|
|||||||
}
|
}
|
||||||
a.style.height = "auto", a.style.height = `${a.scrollHeight}px`;
|
a.style.height = "auto", a.style.height = `${a.scrollHeight}px`;
|
||||||
}
|
}
|
||||||
function pt(a) {
|
function St(a) {
|
||||||
a.key === "Enter" && a.preventDefault();
|
a.key === "Enter" && a.preventDefault();
|
||||||
}
|
}
|
||||||
function _t(a) {
|
function vt(a) {
|
||||||
if (!(a instanceof HTMLTextAreaElement)) {
|
if (!(a instanceof HTMLTextAreaElement)) {
|
||||||
console.warn("HookupTextareaAutoResize: Provided element is not a textarea.");
|
console.warn("HookupTextareaAutoResize: Provided element is not a textarea.");
|
||||||
return;
|
return;
|
||||||
@@ -2285,7 +2407,7 @@ function _t(a) {
|
|||||||
I(a);
|
I(a);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function Pe(a) {
|
function Ye(a) {
|
||||||
if (!(a instanceof HTMLTextAreaElement)) {
|
if (!(a instanceof HTMLTextAreaElement)) {
|
||||||
console.warn("DisconnectTextareaAutoResize: Provided element is not a textarea.");
|
console.warn("DisconnectTextareaAutoResize: Provided element is not a textarea.");
|
||||||
return;
|
return;
|
||||||
@@ -2294,54 +2416,55 @@ function Pe(a) {
|
|||||||
I(a);
|
I(a);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function He(a) {
|
function Ze(a) {
|
||||||
!(a instanceof HTMLTextAreaElement) && a.classList.contains("no-enter") || a.addEventListener("keydown", pt);
|
!(a instanceof HTMLTextAreaElement) && a.classList.contains("no-enter") || a.addEventListener("keydown", St);
|
||||||
}
|
}
|
||||||
function qe(a) {
|
function ti(a) {
|
||||||
!(a instanceof HTMLTextAreaElement) && a.classList.contains("no-enter") || a.removeEventListener("keydown", pt);
|
!(a instanceof HTMLTextAreaElement) && a.classList.contains("no-enter") || a.removeEventListener("keydown", St);
|
||||||
}
|
}
|
||||||
function Ue(a, i) {
|
function ei(a, i) {
|
||||||
for (const t of a)
|
for (const t of a)
|
||||||
if (t.type === "childList") {
|
if (t.type === "childList") {
|
||||||
for (const e of t.addedNodes)
|
for (const e of t.addedNodes)
|
||||||
e.nodeType === Node.ELEMENT_NODE && e.matches("textarea") && (_t(e), I(e));
|
e.nodeType === Node.ELEMENT_NODE && e.matches("textarea") && (vt(e), I(e));
|
||||||
for (const e of t.removedNodes)
|
for (const e of t.removedNodes)
|
||||||
e.nodeType === Node.ELEMENT_NODE && e.matches("textarea") && (qe(e), Pe(e));
|
e.nodeType === Node.ELEMENT_NODE && e.matches("textarea") && (ti(e), Ye(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function Fe(a) {
|
function ii(a) {
|
||||||
if (!(a instanceof HTMLFormElement)) {
|
if (!(a instanceof HTMLFormElement)) {
|
||||||
console.warn("FormLoad: Provided element is not a form.");
|
console.warn("FormLoad: Provided element is not a form.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const i = document.querySelectorAll("textarea");
|
const i = document.querySelectorAll("textarea");
|
||||||
for (const s of i)
|
for (const s of i)
|
||||||
_t(s), I(s);
|
vt(s), I(s);
|
||||||
const t = document.querySelectorAll("textarea.no-enter");
|
const t = document.querySelectorAll("textarea.no-enter");
|
||||||
for (const s of t)
|
for (const s of t)
|
||||||
He(s);
|
Ze(s);
|
||||||
new MutationObserver(Ue).observe(a, {
|
new MutationObserver(ei).observe(a, {
|
||||||
childList: !0,
|
childList: !0,
|
||||||
subtree: !0
|
subtree: !0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
window.ShowBoostedErrors = Ne;
|
window.ShowBoostedErrors = Je;
|
||||||
window.GenQRCode = Be;
|
window.GenQRCode = je;
|
||||||
window.SelectableInput = $e;
|
window.SelectableInput = Qe;
|
||||||
window.PathPlusQuery = ke;
|
window.PathPlusQuery = Ge;
|
||||||
window.HookupRBChange = De;
|
window.HookupRBChange = Xe;
|
||||||
window.FormLoad = Fe;
|
window.FormLoad = ii;
|
||||||
export {
|
export {
|
||||||
f as AbbreviationTooltips,
|
f as AbbreviationTooltips,
|
||||||
vt as FilterList,
|
wt as FilterList,
|
||||||
Et as FilterPill,
|
It as FilterPill,
|
||||||
wt as ImageReel,
|
Bt as ImageReel,
|
||||||
Ct as IntLink,
|
Rt as IntLink,
|
||||||
be as ItemsEditor,
|
Te as ItemsEditor,
|
||||||
dt as MultiSelectRole,
|
ft as MultiSelectRole,
|
||||||
ct as MultiSelectSimple,
|
gt as MultiSelectSimple,
|
||||||
It as PopupImage,
|
Ot as PopupImage,
|
||||||
At as ScrollButton,
|
xt as ScrollButton,
|
||||||
yt as TabList,
|
Oe as SingleSelectRemote,
|
||||||
Tt as ToolTip
|
kt as TabList,
|
||||||
|
Mt as ToolTip
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -274,9 +274,14 @@ type AlmanachResult struct {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div class="flex items-center gap-2 text-lg font-bold text-gray-700">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<i class="ri-links-line"></i>
|
<div class="flex items-center gap-2 text-lg font-bold text-gray-700">
|
||||||
<span>Normdaten & Verknüpfungen</span>
|
<i class="ri-links-line"></i>
|
||||||
|
<span>Normdaten & Verknüpfungen</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="agents-add-toggle" class="text-gray-700 hover:text-gray-900">
|
||||||
|
<i class="ri-add-line"></i> Akteur hinzufügen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr class="border-slate-400 mt-2 mb-3" />
|
<hr class="border-slate-400 mt-2 mb-3" />
|
||||||
<div class="flex flex-col gap-4 mt-4">
|
<div class="flex flex-col gap-4 mt-4">
|
||||||
@@ -291,7 +296,7 @@ type AlmanachResult struct {
|
|||||||
data-result-key="places"
|
data-result-key="places"
|
||||||
data-minchars="1"
|
data-minchars="1"
|
||||||
data-limit="15">
|
data-limit="15">
|
||||||
</multi-select-simple>
|
</multi-select-simple>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -316,6 +321,238 @@ type AlmanachResult struct {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="text-base font-semibold text-gray-700">
|
||||||
|
<span>Personen & Körperschaften</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 mt-4">
|
||||||
|
{{- if $model.result.EntriesAgents -}}
|
||||||
|
{{- range $i, $r := $model.result.EntriesAgents -}}
|
||||||
|
{{- $a := index $model.result.Agents $r.Agent -}}
|
||||||
|
<div class="border border-stone-200 rounded-xs bg-stone-50 px-3 py-2">
|
||||||
|
<div class="grid grid-cols-[1fr_14rem_8rem_7rem] gap-3 items-center">
|
||||||
|
<div class="flex flex-col min-w-0">
|
||||||
|
{{- if $a -}}
|
||||||
|
<a href="/person/{{ $a.Id }}" class="text-base font-bold text-gray-800 no-underline hover:text-slate-900 truncate">
|
||||||
|
{{- $a.Name -}}
|
||||||
|
</a>
|
||||||
|
{{- if $a.BiographicalData -}}
|
||||||
|
<div class="text-xs text-gray-600 truncate">{{- $a.BiographicalData -}}</div>
|
||||||
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
<div class="text-base font-bold text-gray-800">Unbekannte Person</div>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select name="entries_agents_type[{{ $r.Id }}]" id="entries_agents_type_{{ $r.Id }}" autocomplete="off" class="inputselect font-bold w-full">
|
||||||
|
{{- range $t := $model.agent_relations -}}
|
||||||
|
<option value="{{- $t -}}" {{ if eq $r.Type $t }}selected{{ end }}>{{- $t -}}</option>
|
||||||
|
{{- end -}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="entries_agents_uncertain[{{ $r.Id }}]"
|
||||||
|
id="entries_agents_uncertain_{{ $r.Id }}"
|
||||||
|
{{ if $r.Uncertain }}checked{{ end }} />
|
||||||
|
<label for="entries_agents_uncertain_{{ $r.Id }}" class="text-sm text-gray-700">Unsicher</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-sm text-red-700 hover:text-red-900"
|
||||||
|
data-delete-toggle="entries_agents_delete_{{ $r.Id }}">
|
||||||
|
<i class="ri-delete-bin-line mr-1"></i> Entfernen
|
||||||
|
</button>
|
||||||
|
<input type="checkbox" class="hidden" name="entries_agents_delete[{{ $r.Id }}]" id="entries_agents_delete_{{ $r.Id }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="entries_agents_id[{{ $r.Id }}]" value="{{ $r.Id }}" />
|
||||||
|
<input type="hidden" name="entries_agents_agent[{{ $r.Id }}]" value="{{ $r.Agent }}" />
|
||||||
|
</div>
|
||||||
|
{{- end -}}
|
||||||
|
{{- else -}}
|
||||||
|
<div class="text-sm text-gray-600">Keine Personen verknüpft.</div>
|
||||||
|
{{- end -}}
|
||||||
|
</div>
|
||||||
|
<div id="agents-add-row" class="mt-2"></div>
|
||||||
|
<div id="agents-add-panel" class="mt-2 hidden">
|
||||||
|
<div class="border border-stone-200 rounded-xs bg-stone-50 px-3 py-2">
|
||||||
|
<div class="grid grid-cols-[1fr_14rem_8rem_7rem] gap-3 items-center">
|
||||||
|
<div class="min-w-0">
|
||||||
|
<label for="agents-add-select" class="sr-only">Akteur suchen</label>
|
||||||
|
<single-select-remote
|
||||||
|
id="agents-add-select"
|
||||||
|
name="entries_agents_new_id"
|
||||||
|
placeholder="Akteur suchen..."
|
||||||
|
data-endpoint="/api/agents/search"
|
||||||
|
data-result-key="agents"
|
||||||
|
data-minchars="1"
|
||||||
|
data-limit="15">
|
||||||
|
</single-select-remote>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="entries_agents_new_type" class="sr-only">Beziehung</label>
|
||||||
|
<select name="entries_agents_new_type" id="entries_agents_new_type" autocomplete="off" class="inputselect font-bold w-full">
|
||||||
|
{{- range $t := $model.agent_relations -}}
|
||||||
|
<option value="{{- $t -}}">{{- $t -}}</option>
|
||||||
|
{{- end -}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input type="checkbox" name="entries_agents_new_uncertain" id="entries_agents_new_uncertain" />
|
||||||
|
<label for="entries_agents_new_uncertain" class="text-sm text-gray-700">Unsicher</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<div class="flex items-center gap-3 text-lg">
|
||||||
|
<button type="button" class="text-gray-700 hover:text-gray-900" id="agents-add-apply" aria-label="Akteur hinzufügen">
|
||||||
|
<i class="ri-check-line"></i>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="text-gray-700 hover:text-gray-900" id="agents-add-close" aria-label="Ausblenden">
|
||||||
|
<i class="ri-close-line"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="agents-add-error" class="text-xs text-red-700 mt-2 hidden">Bitte Akteur auswählen.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="module">
|
||||||
|
const agentsAddToggle = document.getElementById("agents-add-toggle");
|
||||||
|
const agentsAddPanel = document.getElementById("agents-add-panel");
|
||||||
|
const agentsAddClose = document.getElementById("agents-add-close");
|
||||||
|
const agentsAddApply = document.getElementById("agents-add-apply");
|
||||||
|
const agentsAddError = document.getElementById("agents-add-error");
|
||||||
|
const agentsAddRow = document.getElementById("agents-add-row");
|
||||||
|
const agentsAddSelect = document.getElementById("agents-add-select");
|
||||||
|
const relationSelect = document.getElementById("entries_agents_new_type");
|
||||||
|
const uncertainCheckbox = document.getElementById("entries_agents_new_uncertain");
|
||||||
|
let pendingAgent = null;
|
||||||
|
if (agentsAddToggle && agentsAddPanel) {
|
||||||
|
agentsAddToggle.addEventListener("click", () => {
|
||||||
|
agentsAddPanel.classList.toggle("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (agentsAddClose && agentsAddPanel) {
|
||||||
|
agentsAddClose.addEventListener("click", () => {
|
||||||
|
agentsAddPanel.classList.add("hidden");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const clearAddPanel = () => {
|
||||||
|
if (agentsAddSelect) {
|
||||||
|
const clearButton = agentsAddSelect.querySelector(".ssr-clear-button");
|
||||||
|
if (clearButton) {
|
||||||
|
clearButton.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (relationSelect) {
|
||||||
|
relationSelect.selectedIndex = 0;
|
||||||
|
}
|
||||||
|
if (uncertainCheckbox) {
|
||||||
|
uncertainCheckbox.checked = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const removeNewRow = () => {
|
||||||
|
if (agentsAddRow) {
|
||||||
|
agentsAddRow.innerHTML = "";
|
||||||
|
}
|
||||||
|
pendingAgent = null;
|
||||||
|
clearAddPanel();
|
||||||
|
};
|
||||||
|
if (agentsAddApply && agentsAddPanel) {
|
||||||
|
agentsAddApply.addEventListener("click", () => {
|
||||||
|
const idInput = agentsAddPanel.querySelector("input[name='entries_agents_new_id']");
|
||||||
|
const hasSelection = idInput && idInput.value.trim().length > 0;
|
||||||
|
if (!hasSelection) {
|
||||||
|
if (agentsAddError) {
|
||||||
|
agentsAddError.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (agentsAddError) {
|
||||||
|
agentsAddError.classList.add("hidden");
|
||||||
|
}
|
||||||
|
if (!pendingAgent || !agentsAddRow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = document.createElement("div");
|
||||||
|
row.className = "border border-stone-200 rounded-xs bg-stone-50 px-3 py-2";
|
||||||
|
row.innerHTML = `
|
||||||
|
<div class="grid grid-cols-[1fr_14rem_8rem_7rem] gap-3 items-center">
|
||||||
|
<div class="flex flex-col min-w-0">
|
||||||
|
<div class="text-base font-bold text-gray-800 truncate">
|
||||||
|
<a href="/person/${pendingAgent.id}" class="no-underline hover:text-slate-900">
|
||||||
|
${pendingAgent.name || ""}
|
||||||
|
</a>
|
||||||
|
<em class="text-sm text-gray-600 ml-1">(Neu)</em>
|
||||||
|
</div>
|
||||||
|
${pendingAgent.bio ? `<div class="text-xs text-gray-600 truncate">${pendingAgent.bio}</div>` : ""}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<select name="entries_agents_new_type" class="inputselect font-bold w-full"></select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input type="checkbox" name="entries_agents_new_uncertain" id="entries_agents_new_uncertain_row" />
|
||||||
|
<label for="entries_agents_new_uncertain_row" class="text-sm text-gray-700">Unsicher</label>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button type="button" class="text-sm text-red-700 hover:text-red-900" data-new-delete>
|
||||||
|
<i class="ri-delete-bin-line mr-1"></i> Entfernen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="entries_agents_new_id" value="${pendingAgent.id}" />
|
||||||
|
`;
|
||||||
|
const rowSelect = row.querySelector("select[name='entries_agents_new_type']");
|
||||||
|
if (rowSelect && relationSelect) {
|
||||||
|
rowSelect.innerHTML = relationSelect.innerHTML;
|
||||||
|
rowSelect.value = relationSelect.value;
|
||||||
|
}
|
||||||
|
const rowUncertain = row.querySelector("input[name='entries_agents_new_uncertain']");
|
||||||
|
if (rowUncertain && uncertainCheckbox) {
|
||||||
|
rowUncertain.checked = uncertainCheckbox.checked;
|
||||||
|
}
|
||||||
|
const deleteButton = row.querySelector("[data-new-delete]");
|
||||||
|
if (deleteButton) {
|
||||||
|
deleteButton.addEventListener("click", () => {
|
||||||
|
removeNewRow();
|
||||||
|
if (agentsAddPanel) {
|
||||||
|
agentsAddPanel.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
agentsAddRow.innerHTML = "";
|
||||||
|
agentsAddRow.appendChild(row);
|
||||||
|
agentsAddPanel.classList.add("hidden");
|
||||||
|
clearAddPanel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (agentsAddSelect) {
|
||||||
|
agentsAddSelect.addEventListener("ssrchange", (event) => {
|
||||||
|
pendingAgent = event.detail?.item || null;
|
||||||
|
if (pendingAgent && agentsAddError) {
|
||||||
|
agentsAddError.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll("[data-delete-toggle]").forEach((button) => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
const targetId = button.getAttribute("data-delete-toggle");
|
||||||
|
const checkbox = document.getElementById(targetId);
|
||||||
|
if (!checkbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
|
button.classList.toggle("opacity-60", checkbox.checked);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- End Left Column -->
|
<!-- End Left Column -->
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { MultiSelectSimple } from "./multi-select-simple.js";
|
|||||||
import { ResetButton } from "./reset-button.js";
|
import { ResetButton } from "./reset-button.js";
|
||||||
import { DivManager } from "./div-menu.js";
|
import { DivManager } from "./div-menu.js";
|
||||||
import { ItemsEditor } from "./items-editor.js";
|
import { ItemsEditor } from "./items-editor.js";
|
||||||
|
import { SingleSelectRemote } from "./single-select-remote.js";
|
||||||
|
|
||||||
const FILTER_LIST_ELEMENT = "filter-list";
|
const FILTER_LIST_ELEMENT = "filter-list";
|
||||||
const SCROLL_BUTTON_ELEMENT = "scroll-button";
|
const SCROLL_BUTTON_ELEMENT = "scroll-button";
|
||||||
@@ -27,6 +28,7 @@ const FILTER_PILL_ELEMENT = "filter-pill";
|
|||||||
const IMAGE_REEL_ELEMENT = "image-reel";
|
const IMAGE_REEL_ELEMENT = "image-reel";
|
||||||
const MULTI_SELECT_ROLE_ELEMENT = "multi-select-places";
|
const MULTI_SELECT_ROLE_ELEMENT = "multi-select-places";
|
||||||
const MULTI_SELECT_SIMPLE_ELEMENT = "multi-select-simple";
|
const MULTI_SELECT_SIMPLE_ELEMENT = "multi-select-simple";
|
||||||
|
const SINGLE_SELECT_REMOTE_ELEMENT = "single-select-remote";
|
||||||
const RESET_BUTTON_ELEMENT = "reset-button";
|
const RESET_BUTTON_ELEMENT = "reset-button";
|
||||||
const DIV_MANAGER_ELEMENT = "div-manager";
|
const DIV_MANAGER_ELEMENT = "div-manager";
|
||||||
const ITEMS_EDITOR_ELEMENT = "items-editor";
|
const ITEMS_EDITOR_ELEMENT = "items-editor";
|
||||||
@@ -42,6 +44,7 @@ customElements.define(FILTER_PILL_ELEMENT, FilterPill);
|
|||||||
customElements.define(IMAGE_REEL_ELEMENT, ImageReel);
|
customElements.define(IMAGE_REEL_ELEMENT, ImageReel);
|
||||||
customElements.define(MULTI_SELECT_ROLE_ELEMENT, MultiSelectRole);
|
customElements.define(MULTI_SELECT_ROLE_ELEMENT, MultiSelectRole);
|
||||||
customElements.define(MULTI_SELECT_SIMPLE_ELEMENT, MultiSelectSimple);
|
customElements.define(MULTI_SELECT_SIMPLE_ELEMENT, MultiSelectSimple);
|
||||||
|
customElements.define(SINGLE_SELECT_REMOTE_ELEMENT, SingleSelectRemote);
|
||||||
customElements.define(RESET_BUTTON_ELEMENT, ResetButton);
|
customElements.define(RESET_BUTTON_ELEMENT, ResetButton);
|
||||||
customElements.define(DIV_MANAGER_ELEMENT, DivManager);
|
customElements.define(DIV_MANAGER_ELEMENT, DivManager);
|
||||||
customElements.define(ITEMS_EDITOR_ELEMENT, ItemsEditor);
|
customElements.define(ITEMS_EDITOR_ELEMENT, ItemsEditor);
|
||||||
@@ -280,4 +283,4 @@ window.PathPlusQuery = PathPlusQuery;
|
|||||||
window.HookupRBChange = HookupRBChange;
|
window.HookupRBChange = HookupRBChange;
|
||||||
window.FormLoad = FormLoad;
|
window.FormLoad = FormLoad;
|
||||||
|
|
||||||
export { FilterList, ScrollButton, AbbreviationTooltips, MultiSelectSimple, MultiSelectRole, ToolTip, PopupImage, TabList, FilterPill, ImageReel, IntLink, ItemsEditor };
|
export { FilterList, ScrollButton, AbbreviationTooltips, MultiSelectSimple, MultiSelectRole, ToolTip, PopupImage, TabList, FilterPill, ImageReel, IntLink, ItemsEditor, SingleSelectRemote };
|
||||||
|
|||||||
286
views/transform/single-select-remote.js
Normal file
286
views/transform/single-select-remote.js
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
const SSR_WRAPPER_CLASS = "ssr-wrapper";
|
||||||
|
const SSR_INPUT_CLASS = "ssr-input";
|
||||||
|
const SSR_LIST_CLASS = "ssr-list";
|
||||||
|
const SSR_OPTION_CLASS = "ssr-option";
|
||||||
|
const SSR_OPTION_NAME_CLASS = "ssr-option-name";
|
||||||
|
const SSR_OPTION_DETAIL_CLASS = "ssr-option-detail";
|
||||||
|
const SSR_OPTION_BIO_CLASS = "ssr-option-bio";
|
||||||
|
const SSR_HIDDEN_INPUT_CLASS = "ssr-hidden-input";
|
||||||
|
const SSR_CLEAR_BUTTON_CLASS = "ssr-clear-button";
|
||||||
|
|
||||||
|
const SSR_DEFAULT_MIN_CHARS = 1;
|
||||||
|
const SSR_DEFAULT_LIMIT = 10;
|
||||||
|
const SSR_FETCH_DEBOUNCE_MS = 250;
|
||||||
|
|
||||||
|
export class SingleSelectRemote extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._endpoint = "";
|
||||||
|
this._resultKey = "items";
|
||||||
|
this._minChars = SSR_DEFAULT_MIN_CHARS;
|
||||||
|
this._limit = SSR_DEFAULT_LIMIT;
|
||||||
|
this._placeholder = "Search...";
|
||||||
|
this._options = [];
|
||||||
|
this._selected = null;
|
||||||
|
this._fetchTimeout = null;
|
||||||
|
this._fetchController = null;
|
||||||
|
this._listVisible = false;
|
||||||
|
this._boundHandleInput = this._handleInput.bind(this);
|
||||||
|
this._boundHandleFocus = this._handleFocus.bind(this);
|
||||||
|
this._boundHandleKeyDown = this._handleKeyDown.bind(this);
|
||||||
|
this._boundHandleClear = this._handleClear.bind(this);
|
||||||
|
this._boundHandleClickOutside = this._handleClickOutside.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ["data-endpoint", "data-result-key", "data-minchars", "data-limit", "placeholder", "name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this._render();
|
||||||
|
this._input = this.querySelector(`.${SSR_INPUT_CLASS}`);
|
||||||
|
this._list = this.querySelector(`.${SSR_LIST_CLASS}`);
|
||||||
|
this._hiddenInput = this.querySelector(`.${SSR_HIDDEN_INPUT_CLASS}`);
|
||||||
|
this._clearButton = this.querySelector(`.${SSR_CLEAR_BUTTON_CLASS}`);
|
||||||
|
|
||||||
|
this._endpoint = this.getAttribute("data-endpoint") || "";
|
||||||
|
this._resultKey = this.getAttribute("data-result-key") || "items";
|
||||||
|
this._minChars = this._parsePositiveInt(this.getAttribute("data-minchars"), SSR_DEFAULT_MIN_CHARS);
|
||||||
|
this._limit = this._parsePositiveInt(this.getAttribute("data-limit"), SSR_DEFAULT_LIMIT);
|
||||||
|
this._placeholder = this.getAttribute("placeholder") || "Search...";
|
||||||
|
|
||||||
|
if (this._input) {
|
||||||
|
this._input.placeholder = this._placeholder;
|
||||||
|
this._input.addEventListener("input", this._boundHandleInput);
|
||||||
|
this._input.addEventListener("focus", this._boundHandleFocus);
|
||||||
|
this._input.addEventListener("keydown", this._boundHandleKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._clearButton) {
|
||||||
|
this._clearButton.addEventListener("click", this._boundHandleClear);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("click", this._boundHandleClickOutside);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
document.removeEventListener("click", this._boundHandleClickOutside);
|
||||||
|
if (this._input) {
|
||||||
|
this._input.removeEventListener("input", this._boundHandleInput);
|
||||||
|
this._input.removeEventListener("focus", this._boundHandleFocus);
|
||||||
|
this._input.removeEventListener("keydown", this._boundHandleKeyDown);
|
||||||
|
}
|
||||||
|
if (this._clearButton) {
|
||||||
|
this._clearButton.removeEventListener("click", this._boundHandleClear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
if (oldValue === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (name === "data-endpoint") this._endpoint = newValue || "";
|
||||||
|
if (name === "data-result-key") this._resultKey = newValue || "items";
|
||||||
|
if (name === "data-minchars") this._minChars = this._parsePositiveInt(newValue, SSR_DEFAULT_MIN_CHARS);
|
||||||
|
if (name === "data-limit") this._limit = this._parsePositiveInt(newValue, SSR_DEFAULT_LIMIT);
|
||||||
|
if (name === "placeholder") {
|
||||||
|
this._placeholder = newValue || "Search...";
|
||||||
|
if (this._input) this._input.placeholder = this._placeholder;
|
||||||
|
}
|
||||||
|
if (name === "name" && this._hiddenInput) this._hiddenInput.name = newValue || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleInput(event) {
|
||||||
|
const value = event.target.value.trim();
|
||||||
|
this._selected = null;
|
||||||
|
this._syncHiddenInput();
|
||||||
|
|
||||||
|
if (value.length < this._minChars) {
|
||||||
|
this._options = [];
|
||||||
|
this._renderOptions();
|
||||||
|
this._hideList();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._debouncedFetch(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleFocus() {
|
||||||
|
if (this._options.length > 0) {
|
||||||
|
this._showList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleKeyDown(event) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
this._hideList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleClear(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
this._selected = null;
|
||||||
|
this._options = [];
|
||||||
|
if (this._input) this._input.value = "";
|
||||||
|
this._syncHiddenInput();
|
||||||
|
this._renderOptions();
|
||||||
|
this._hideList();
|
||||||
|
this.dispatchEvent(new CustomEvent("ssrchange", { bubbles: true, detail: { item: null } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleClickOutside(event) {
|
||||||
|
if (!this.contains(event.target)) {
|
||||||
|
this._hideList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_debouncedFetch(query) {
|
||||||
|
if (this._fetchTimeout) {
|
||||||
|
clearTimeout(this._fetchTimeout);
|
||||||
|
}
|
||||||
|
this._fetchTimeout = setTimeout(() => {
|
||||||
|
this._fetchOptions(query);
|
||||||
|
}, SSR_FETCH_DEBOUNCE_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _fetchOptions(query) {
|
||||||
|
if (!this._endpoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._fetchController) {
|
||||||
|
this._fetchController.abort();
|
||||||
|
}
|
||||||
|
this._fetchController = new AbortController();
|
||||||
|
const url = new URL(this._endpoint, window.location.origin);
|
||||||
|
url.searchParams.set("q", query);
|
||||||
|
if (this._limit > 0) {
|
||||||
|
url.searchParams.set("limit", String(this._limit));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const resp = await fetch(url.toString(), { signal: this._fetchController.signal });
|
||||||
|
if (!resp.ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = await resp.json();
|
||||||
|
const items = Array.isArray(data?.[this._resultKey]) ? data[this._resultKey] : [];
|
||||||
|
this._options = items.filter((item) => item && item.id && item.name);
|
||||||
|
this._renderOptions();
|
||||||
|
if (this._options.length > 0) {
|
||||||
|
this._showList();
|
||||||
|
} else {
|
||||||
|
this._hideList();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err?.name === "AbortError") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderOptions() {
|
||||||
|
if (!this._list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._list.innerHTML = "";
|
||||||
|
|
||||||
|
this._options.forEach((item) => {
|
||||||
|
const option = document.createElement("button");
|
||||||
|
option.type = "button";
|
||||||
|
option.className = [
|
||||||
|
SSR_OPTION_CLASS,
|
||||||
|
"w-full text-left px-3 py-2 hover:bg-slate-100 transition-colors",
|
||||||
|
].join(" ");
|
||||||
|
|
||||||
|
const nameEl = document.createElement("div");
|
||||||
|
nameEl.className = [SSR_OPTION_NAME_CLASS, "text-sm font-semibold text-gray-800"].join(" ");
|
||||||
|
nameEl.textContent = item.name;
|
||||||
|
option.appendChild(nameEl);
|
||||||
|
|
||||||
|
if (item.detail) {
|
||||||
|
const detailEl = document.createElement("div");
|
||||||
|
detailEl.className = [SSR_OPTION_DETAIL_CLASS, "text-xs text-gray-600"].join(" ");
|
||||||
|
detailEl.textContent = item.detail;
|
||||||
|
option.appendChild(detailEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.bio) {
|
||||||
|
const bioEl = document.createElement("div");
|
||||||
|
bioEl.className = [SSR_OPTION_BIO_CLASS, "text-xs text-gray-500"].join(" ");
|
||||||
|
bioEl.textContent = item.bio;
|
||||||
|
option.appendChild(bioEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
option.addEventListener("click", () => {
|
||||||
|
this._selectOption(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._list.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectOption(item) {
|
||||||
|
this._selected = item;
|
||||||
|
if (this._input) {
|
||||||
|
this._input.value = item.name || "";
|
||||||
|
}
|
||||||
|
this._syncHiddenInput();
|
||||||
|
this._hideList();
|
||||||
|
this.dispatchEvent(new CustomEvent("ssrchange", { bubbles: true, detail: { item } }));
|
||||||
|
this.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
_syncHiddenInput() {
|
||||||
|
if (!this._hiddenInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._hiddenInput.value = this._selected?.id || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
_showList() {
|
||||||
|
if (!this._list || this._listVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._list.classList.remove("hidden");
|
||||||
|
this._listVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideList() {
|
||||||
|
if (!this._list || !this._listVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._list.classList.add("hidden");
|
||||||
|
this._listVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_parsePositiveInt(value, fallback) {
|
||||||
|
const parsed = parseInt(value || "", 10);
|
||||||
|
if (Number.isNaN(parsed) || parsed <= 0) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_render() {
|
||||||
|
const inputName = this.getAttribute("name") || "";
|
||||||
|
this.innerHTML = `
|
||||||
|
<div class="${SSR_WRAPPER_CLASS} relative">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="${SSR_INPUT_CLASS} inputinput w-full"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
autocapitalize="none"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder="${this._placeholder}"
|
||||||
|
/>
|
||||||
|
<button type="button" class="${SSR_CLEAR_BUTTON_CLASS} text-sm text-gray-600 hover:text-gray-900">
|
||||||
|
<i class="ri-close-line"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" class="${SSR_HIDDEN_INPUT_CLASS}" name="${inputName}" value="" />
|
||||||
|
<div class="${SSR_LIST_CLASS} absolute left-0 right-0 mt-1 border border-stone-200 rounded-xs bg-white shadow-sm z-10 hidden max-h-64 overflow-auto"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user