import { elementFromHtml } from "./element-from-html"; export type FormCollectionOptions = { entrySelector: string; addLabel: string; minEntries: number; maxEntries: number; }; const defaults: FormCollectionOptions = { addLabel: "Add", entrySelector: ".palmtree-form-collection-entries > .palmtree-form-collection-entry", minEntries: 0, maxEntries: -1, }; document.addEventListener("DOMContentLoaded", () => { formCollection(document.querySelectorAll("[data-palmtree-form-collection]")); document.body.addEventListener("palmtreeFormCollection.add", (event) => { formCollection((event as CustomEvent).detail.$entry.querySelectorAll("[data-palmtree-form-collection]")); }); }); export function formCollection(widgets: NodeListOf): void { widgets.forEach((widget) => { new FormCollection(widget); }); } class FormCollection { private readonly options: FormCollectionOptions; private widget: HTMLElement; private addButton: HTMLButtonElement; constructor(widget: HTMLElement) { this.widget = widget; this.options = { ...defaults, ...JSON.parse(this.widget.dataset.palmtreeFormCollection || ""), }; this.addButton = this.createAddButton(); this.widget.querySelectorAll(this.options.entrySelector).forEach((entry) => { this.createRemoveButton(entry); }); if (this.options.minEntries > 0 && this.length < this.options.minEntries) { for (let i = 0; i < this.options.minEntries; i++) { this.addEntry(); } } } get length(): number { return this.widget.querySelectorAll(this.options.entrySelector).length; } addEntry() { if (this.options.maxEntries > -1 && this.length >= this.options.maxEntries) { return; } const index = this.widget.dataset.index || this.length; const html = this.widget.dataset.prototype?.replace(/\[-1]/g, `[${index}]`).replace(/(id|for)="(\S+)--1"/g, `$1="$2-${index}"`) || ""; const entry = elementFromHtml(html); this.createRemoveButton(entry); this.widget.querySelector(".palmtree-form-collection-entries")?.append(entry); this.widget.dataset.index = (parseInt(index.toString()) + 1).toString(); const event = new CustomEvent("palmtreeFormCollection.add", { detail: { entry }, }); this.widget.dispatchEvent(event); if (this.options.maxEntries > -1 && this.length >= this.options.maxEntries) { this.addButton.disabled = true; } } removeEntry(entry: Element) { if (this.options.minEntries > 0 && this.length <= this.options.minEntries) { return; } entry.remove(); const event = new CustomEvent("palmtreeFormCollection.remove", { detail: { entry }, }); this.widget.dispatchEvent(event); if (this.options.maxEntries > -1 && this.length < this.options.maxEntries) { this.addButton.disabled = false; } } createAddButton(): HTMLButtonElement { const addButton = elementFromHtml(``); addButton.addEventListener("click", () => { this.addEntry(); }); const wrapper = elementFromHtml('
'); wrapper.append(addButton); this.widget.append(wrapper); return addButton; } createRemoveButton(entry: Element) { const removeButton = elementFromHtml(''); entry.append(removeButton); removeButton.addEventListener("click", () => { this.removeEntry(entry); }); } }