You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

269 lines
10 KiB

/**
* 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()
})
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() })
this.get
}
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 = ""
}
}
/**
* 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"
}
/**
* Deletes the selected column from the table.
*/
delCol() {
let rows = Array.from(this.tableBody.children)
let rowCount = rows.length
let colCount = this.tableBody.lastElementChild.children.length
// Shift existing column data to the left.
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
}
}
// Shift existing column titles to the left.
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
}
// Shift existing column colors to the left.
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
}
// Remove the last cell from each row.
rows.forEach(row => {
row.removeChild(row.lastElementChild)
})
// Remove the last header cell.
this.tableHead.lastElementChild.removeChild(this.tableHead.lastElementChild.lastElementChild)
}
}

Powered by TurnKey Linux.