Source: ui/table/EditableTable.js

import { UiElement } from "../UiElement.js";
import "./EditableTable.css";

/**
 * Represents an editable table UI element.
 * @extends UiElement
 * @memberof vef.ui.table
 */
class EditableTable extends UiElement {

    classes = "editable-table";
    template = `
        <table>
            <thead>
                <tr></tr>
            </thead>
            <tbody></tbody>
        </table>
        <a href="#" class="btn-add-row">+ Add</a>
    `;

    /**
     * Creates an instance of EditableTable.
     * @param {HTMLElement} target - The target element to attach the table to.
     * @param {Array} columns - The columns of the table.
     * @param {Array} values - The initial values for the table rows.
     */
    constructor(target, columns, values) {
        super(target);

        this.columns = columns || [];
        this.datalists = {};

        this.setHtml(this.template);
        this.setClass(this.classes);

        this.tbody = this.query("tbody");
        this.addRowButton = this.query(".btn-add-row");
        this.addRowButton.addEventListener("click", (e) => {
            e.preventDefault();
            this.addRow();
        });

        this.getElement().classList.add("editable-table");
        this.getElement().appendChild(this.addRowButton);

        this.columns.forEach(column => {
            this.query("thead tr").insertAdjacentHTML("beforeend", `<td>${column.name}</td>`)
            this.extractAndCreateDataLists(values, column.key, column.name);
        });

        this.Row(values);
    }

    /**
     * Extracts values from an array of objects and creates datalists for a specific column.
     * @param {Array} array - The array of objects.
     * @param {string} key - The key to extract values from.
     * @param {string} name - The name of the column.
     */
    extractAndCreateDataLists(array, key, name) {
        const values = this.createArrayValues(array, key);
        this.createList(values, name);
    }

    /**
     * Creates an array of values from an array of objects based on a specific key.
     * @param {Array} ar - The array of objects.
     * @param {string} key - The key to extract values from.
     * @returns {Array} The array of values.
     */
    createArrayValues(ar, key) {
        return ar.map(obj => obj[key]);
    }

    /**
     * Creates a datalist for a specific column.
     * @param {Array} arr - The array of values.
     * @param {string} name - The name of the column.
     */
    createList(arr, name) {
        const uniArr = [...new Set(arr)];
        const dataList = document.createElement("datalist");
        dataList.id = name;

        uniArr.forEach(element => {
            const option = document.createElement('option');
            option.value = element;
            option.text = element;
            dataList.appendChild(option);
        });
        this.getElement().appendChild(dataList);
        this.datalists[name] = dataList;
    }

    /**
     * Adds a new row to the table.
     */
    addRow() {
        const tr = document.createElement("tr");
        this.columns.forEach(column => {
            const listName = column.key;
            const td = document.createElement("td");
            const input = document.createElement("input");
            input.type = 'search';
            input.setAttribute('list', listName);
            td.appendChild(input);
            tr.appendChild(td);
        });
        const td = document.createElement("td");
        const deleteButton = document.createElement("a");
        deleteButton.href = "#";
        deleteButton.addEventListener("click", e => {
            e.preventDefault();
            e.currentTarget.parentNode.parentNode.remove();
        })
        deleteButton.className = "EditableTableRowRemove"
        deleteButton.innerHTML = "<i class='fas fa-times'></i>";
        td.appendChild(deleteButton);
        tr.appendChild(td);
        this.tbody.appendChild(tr);
    }

    /**
     * Adds rows to the table based on an array of existing values.
     * @param {Array} array_of_existing - The array of existing values.
     */
    Row(array_of_existing) {
        array_of_existing.forEach(obj => {
            const tr = document.createElement("tr");
            let table_rows = this.columns.map(column => column.key);
            let countr = 0;
            table_rows.forEach(listName => {
                const td = document.createElement("td");
                const input = document.createElement("input");
                input.type = 'search';
                input.setAttribute('list', listName);
                input.value = obj[table_rows[countr]]
                td.appendChild(input);
                tr.appendChild(td);
                ++countr;
            });
            const td = document.createElement("td");
            const deleteButton = document.createElement("a");
            deleteButton.href = "#";
            deleteButton.className = "EditableTableRowRemove"
            deleteButton.addEventListener("click", e => {
                e.preventDefault();
                e.currentTarget.parentNode.parentNode.remove();
            })
            deleteButton.innerHTML = "<i class='fas fa-times'></i>";
            td.appendChild(deleteButton);
            tr.appendChild(td);
            this.tbody.appendChild(tr);
        });
    }

    /**
     * Gets the values from the table.
     * @returns {Array} The array of values.
     */
    getValues() {
        const rows = this.tbody.querySelectorAll("tr");
        const valuesArray = [];

        rows.forEach(row => {
            const inputs = row.querySelectorAll("input[type='search']");
            const values = {};

            inputs.forEach(input => {
                const columnname = input.getAttribute('list');
                if (columnname) {
                    values[columnname] = input.value;
                }
            });

            valuesArray.push(values);
        })

        return valuesArray;
    }

    /**
     * Clears the table.
     */
    clearTable() {
        const trs = this.tbody.querySelectorAll("tr");
        trs.forEach(tr => {
            tr.remove();
        })
    }

}

export { EditableTable };