/** * Represents a table object. */ class Table { /** * Initializes a new instance of the Table class. * @param {HTMLElement} tableElement - The HTML table element. * @param {HTMLElement} rcMenu - The right-click menu element. */ constructor(tableElement, rcMenu) { this.tableElement = tableElement this.tableBody = tableElement.querySelector("tbody") this.tableHead = tableElement.querySelector("thead") this.rcMenu = rcMenu // Initialize right-click menu options this.rcAddRow = rcMenu.querySelector("#rcAddRow") this.rcDelRow = rcMenu.querySelector("#rcDelRow") this.rcAddCol = rcMenu.querySelector("#rcAddCol") this.rcDelCol = rcMenu.querySelector("#rcDelCol") this.selectedCell = null this.addEventListeners(this.rcMenu, this.tableElement) } addEventListeners() { // Hide context menu when mouse button is clicked anywhere on the window window.addEventListener('mousedown', (e) => { this.rcMenu.style.display = "none" }) // Show context menu when right-clicking on the data table this.tableElement.addEventListener('contextmenu', (e) => { const pos = { x: e.clientX, y: e.clientY } this.handleContextMenu(this.rcMenu, this.tableElement, pos) this.selectedCell = e.target // Prevent default context menu from appearing e.preventDefault() e.stopPropagation() }) // Add click event listeners to toggle display of settings submenus let labels = [...document.getElementsByClassName("submenuLabel")] labels.forEach(label => { label.addEventListener('click', e => { let submenuDiv = e.target.nextElementSibling // Toggle display of submenu if (getComputedStyle(submenuDiv).display == "block") submenuDiv.style.display = "none" else submenuDiv.style.display = "block" }) }) this.rcAddRow.addEventListener("mousedown", (e) => { this.addRow() }) this.rcDelRow.addEventListener("mousedown", (e) => { this.delRow() }) this.rcAddCol.addEventListener("mousedown", (e) => { this.addCol() }) this.rcDelCol.addEventListener("mousedown", (e) => { this.delCol() }) } handleContextMenu(rcMenu, tableElement, pos) { rcMenu.style.display = "block" // Position the context menu relative to the mouse pointer if (pos.x + rcMenu.clientWidth <= tableElement.clientWidth) rcMenu.style.left = pos.x + "px" else rcMenu.style.left = pos.x - rcMenu.clientWidth + "px" if (pos.y + rcMenu.clientHeight <= window.innerHeight + document.documentElement.scrollTop) rcMenu.style.top = pos.y + "px" else rcMenu.style.top = pos.y - rcMenu.clientHeight + "px" } /** * Gets the position of the cell in the table. * @param {HTMLElement} cell - The HTML cell element. * @returns {Object} An object containing the column and row indexes. */ getCellPos(cell) { // Name contains the column and row indexes. let name = cell.name let match = name.match(/\d+/g) let col = +match[0] let row = +match[1] row = isNaN(row) ? -1 : row return { col: col, row: row } } /** * Adds a new row to the table below the selected cell. */ addRow() { // Clone the last row to create a new row. let lastRow = this.tableBody.lastElementChild let newRow = lastRow.cloneNode(true) // Get all input cells in the new row. let cells = Array.from(newRow.children).map(cell => cell.querySelector("input")) // Determine the row index of the last row. let lastRowIndex = this.getCellPos(cells[0]).row // Update IDs and names of input cells in the new row. cells.forEach(cell => { let pos = this.getCellPos(cell) cell.id = `chart_table_${pos.col}_values_${pos.row + 1}` cell.name = `chart[table][${pos.col}][values][${pos.row + 1}]` }) // Shift existing row data down to make room for a new row. let pos = this.getCellPos(this.selectedCell) //pos.row = pos.row === -1 ? 0 : pos.row pos.row++ for (let i = lastRowIndex; i > pos.row; i--) { for (let j = 0; j < cells.length; j++) { let currentCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i}`) let previousCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i - 1}`) currentCell.value = previousCell.value } } this.tableBody.appendChild(newRow) // Clear input cells in the new row. for (let j = 0; j < cells.length; j++) { let currentCell = this.tableBody.querySelector(`#chart_table_${j}_values_${pos.row}`) currentCell.value = "" console.log(currentCell) } } /** * Deletes the row containing the selected cell. */ delRow() { // Get the number of rows and columns in the table. let rowCount = this.tableBody.children.length let colCount = this.tableBody.lastElementChild.children.length // Get the position of the selected cell. let pos = this.getCellPos(this.selectedCell) // Don't delete heading row. if (pos.row === -1) return // Shift row data for (let i = pos.row; i < rowCount - 1; i++) { for (let j = 0; j < colCount; j++) { let currentCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i}`) let previousCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i + 1}`) currentCell.value = previousCell.value } } // Remove the last row from the table. this.tableBody.removeChild(this.tableBody.lastElementChild) } /** * Adds a new column to the table. */ addCol() { // Get the rows and column count of the table. let rows = Array.from(this.tableBody.children) let rowCount = rows.length let colCount = this.tableBody.lastElementChild.children.length // Clone the last cell in the heading row to create a new header cell for the new column. let newHeadCell = this.tableHead.lastElementChild.lastElementChild.cloneNode(true) let newHeadCellInput = newHeadCell.querySelector("input[type='text']") newHeadCellInput.id = `chart_table_${colCount}_col_name` newHeadCellInput.name = `chart[table][${colCount}][col_name]` let newHeadCellColor = newHeadCell.querySelector("input[type='color']") newHeadCellColor.id = `chart_table_${colCount}_color` newHeadCellColor.name = `chart[table][${colCount}][color]` this.tableHead.lastElementChild.appendChild(newHeadCell) // Clone the last cell in each row to create a new cell for the new column in each row. for (let i = 0; i < rowCount; i++) { let newCell = rows[i].lastElementChild.cloneNode(true) let newCellInput = newCell.querySelector("input") newCellInput.id = `chart_table_${colCount}_values_${i}` newCellInput.name = `chart[table][${colCount}][values][${i}]` rows[i].appendChild(newCell) } // Shift existing column data to the right to make room for a new column. let pos = this.getCellPos(this.selectedCell) pos.col++ for (let i = 0; i < rowCount; i++) { for (let j = colCount; j > pos.col; j--) { let currentCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i}`) let previousCell = this.tableBody.querySelector(`#chart_table_${j - 1}_values_${i}`) currentCell.value = previousCell.value } } // Shift existing column headings to the right to make room for a new column heading. for (let j = colCount; j > pos.col; j--) { let currentCell = this.tableHead.querySelector(`#chart_table_${j}_col_name`) let previousCell = this.tableHead.querySelector(`#chart_table_${j - 1}_col_name`) currentCell.value = previousCell.value } // Shift existing column colors to the right to make room for a new column color. for (let j = colCount; j > pos.col; j--) { let currentCell = this.tableHead.querySelector(`#chart_table_${j}_color`) let previousCell = this.tableHead.querySelector(`#chart_table_${j - 1}_color`) currentCell.value = previousCell.value } // Clear input cells in the new column. for (let i = 0; i < rowCount; i++) { let currentCell = this.tableBody.querySelector(`#chart_table_${pos.col}_values_${i}`) currentCell.value = "" } // Clear the input and color of the new column heading. let headCell = this.tableHead.lastElementChild.children[pos.col] headCell.querySelector("input[type='text']").value = "" headCell.querySelector("input[type='color']").value = "#FFFFFF" } delCol() { let rows = Array.from(this.tableBody.children) let rowCount = rows.length let colCount = this.tableBody.lastElementChild.children.length let pos = this.getCellPos(this.selectedCell) for (let i = 0; i < rowCount; i++) { for (let j = pos.col; j < colCount - 1; j++) { let currentCell = this.tableBody.querySelector(`#chart_table_${j}_values_${i}`) let previousCell = this.tableBody.querySelector(`#chart_table_${j + 1}_values_${i}`) currentCell.value = previousCell.value } } for (let i = pos.col; i < colCount - 1; i++) { let currentCell = this.tableHead.querySelector(`#chart_table_${i}_col_name`) let previousCell = this.tableHead.querySelector(`#chart_table_${i + 1}_col_name`) currentCell.value = previousCell.value } for (let i = pos.col; i < colCount - 1; i++) { let currentCell = this.tableHead.querySelector(`#chart_table_${i}_color`) let previousCell = this.tableHead.querySelector(`#chart_table_${i + 1}_color`) currentCell.value = previousCell.value } rows.forEach(row => { row.removeChild(row.lastElementChild) }) this.tableHead.lastElementChild.removeChild(this.tableHead.lastElementChild.lastElementChild) } }