|
// retoor <retoor@molodetz.nl>
|
|
|
|
import { Component } from "./Component.js";
|
|
import { assetUrl } from "../assetVersion.js";
|
|
|
|
const CSS_ID = "ai-choice-multi-css";
|
|
|
|
export class AiChoiceMulti extends Component {
|
|
static get observedAttributes() {
|
|
return ["name", "label", "help", "required", "disabled", "min-selected", "max-selected"];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this._values = new Set();
|
|
}
|
|
|
|
_ensureCss() {
|
|
if (document.getElementById(CSS_ID)) return;
|
|
const link = document.createElement("link");
|
|
link.id = CSS_ID;
|
|
link.rel = "stylesheet";
|
|
link.href = assetUrl("/static/css/components/ai-choice.css");
|
|
document.head.appendChild(link);
|
|
}
|
|
|
|
connectedCallback() {
|
|
this._ensureCss();
|
|
if (this._built) return;
|
|
this._built = true;
|
|
this.classList.add("ai-choice-multi");
|
|
const name = this.attr("name", "choices");
|
|
const label = this.attr("label", "Choices");
|
|
const options = [...this.querySelectorAll("ai-option")].map((el) => ({
|
|
value: el.getAttribute("value") || "",
|
|
label: el.getAttribute("label") || el.textContent || "",
|
|
description: el.getAttribute("description") || "",
|
|
disabled: el.hasAttribute("disabled"),
|
|
selected: el.hasAttribute("selected"),
|
|
}));
|
|
this.innerHTML = "";
|
|
const title = document.createElement("div");
|
|
title.className = "ai-widget-label";
|
|
title.textContent = label;
|
|
this.appendChild(title);
|
|
if (this.attr("help")) {
|
|
const help = document.createElement("div");
|
|
help.className = "ai-widget-help";
|
|
help.textContent = this.attr("help");
|
|
this.appendChild(help);
|
|
}
|
|
const group = document.createElement("div");
|
|
group.className = "ai-choice-options multi";
|
|
for (const option of options) {
|
|
const labelEl = document.createElement("label");
|
|
labelEl.className = "ai-choice-check";
|
|
const input = document.createElement("input");
|
|
input.type = "checkbox";
|
|
input.value = option.value;
|
|
input.disabled = option.disabled;
|
|
if (option.selected) {
|
|
input.checked = true;
|
|
this._values.add(option.value);
|
|
}
|
|
input.addEventListener("change", () => {
|
|
if (input.checked) this._values.add(option.value);
|
|
else this._values.delete(option.value);
|
|
this.dispatchEvent(
|
|
new CustomEvent("ai:change", {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: { name, value: this.getValue() },
|
|
})
|
|
);
|
|
});
|
|
const span = document.createElement("span");
|
|
span.textContent = option.label;
|
|
labelEl.append(input, span);
|
|
if (option.description) {
|
|
const desc = document.createElement("small");
|
|
desc.textContent = option.description;
|
|
labelEl.appendChild(desc);
|
|
}
|
|
group.appendChild(labelEl);
|
|
}
|
|
this.appendChild(group);
|
|
}
|
|
|
|
getValue() {
|
|
return [...this._values];
|
|
}
|
|
|
|
validate() {
|
|
const min = this.intAttr("min-selected", this.boolAttr("required") ? 1 : 0);
|
|
const max = this.intAttr("max-selected", 0);
|
|
const count = this._values.size;
|
|
if (count < min) {
|
|
return { valid: false, message: `${this.attr("label", "Choices")}: select at least ${min}` };
|
|
}
|
|
if (max > 0 && count > max) {
|
|
return { valid: false, message: `${this.attr("label", "Choices")}: select at most ${max}` };
|
|
}
|
|
return { valid: true };
|
|
}
|
|
}
|
|
|
|
if (!customElements.get("ai-choice-multi")) {
|
|
customElements.define("ai-choice-multi", AiChoiceMulti);
|
|
}
|
|
|
|
export default AiChoiceMulti;
|