diff --git a/public/userarea/gridRenderer.js b/public/userarea/gridRenderer.js index a6d0895..004a667 100644 --- a/public/userarea/gridRenderer.js +++ b/public/userarea/gridRenderer.js @@ -36,6 +36,15 @@ return d.innerHTML; } + function escAttr(str) { + if (str === null || str === undefined) return ""; + return String(str) + .replace(/&/g, "&") + .replace(/"/g, """) + .replace(/'/g, "'") + .replace(//g, ">"); + } function getDetailValue(rowIndex, mappingId) { return data[rowIndex].details[String(mappingId)] ?? ""; } @@ -519,7 +528,7 @@ case "tested_component": div.style.overflow = "visible"; div.innerHTML = `
- + @@ -565,7 +574,7 @@ const cls = col.isManual ? "manual-input" : "auto-input"; const reqCls = col.isRequired ? " required-input" : ""; const req = col.isRequired ? " required" : ""; - const v = esc(value); + const v = escAttr(value); if (col.dataType === "SceltaMultipla") { const options = buildDropdownOptionsHTML(col.fieldId, value); @@ -590,7 +599,7 @@ if (col.dataType === "DATE") { const reqCls = col.isRequired ? " required-input" : ""; const req = col.isRequired ? " required" : ""; - return ``; + return ``; } // Client-sourced fields → AJAX Select2 (like idclient) @@ -603,7 +612,7 @@ const label = clientNameCache[value] || value; opts += ``; } - return ``; + return ``; } // Select — build from cache @@ -621,7 +630,7 @@ const reqCls = col.isRequired ? " required-input" : ""; const req = col.isRequired ? " required" : ""; - return ``; + return ``; } function buildDropdownOptionsHTML(fieldId, selectedValue) { @@ -826,6 +835,75 @@ flatpickr(this, { dateFormat: "Y-m-d", allowInput: true }); }); } + function getInputTextWidth(input) { + const span = document.createElement("span"); + const style = window.getComputedStyle(input); + + span.style.position = "absolute"; + span.style.visibility = "hidden"; + span.style.whiteSpace = "pre"; + span.style.font = style.font; + span.style.fontSize = style.fontSize; + span.style.fontFamily = style.fontFamily; + span.style.fontWeight = style.fontWeight; + span.textContent = input.value || input.placeholder || ""; + + document.body.appendChild(span); + + const width = span.offsetWidth + 60; + + document.body.removeChild(span); + + return width; + } + + function autoExpandColumnFromInput(input) { + if (!input) return; + + const cell = input.closest(".grid-cell"); + if (!cell || !cell.dataset.index) return; + + const columnIndex = parseInt(cell.dataset.index, 10); + if (!columnIndex) return; + + const wantedWidth = Math.max(120, getInputTextWidth(input)); + const currentWidth = cell.offsetWidth || 0; + + // Only expand, do not shrink automatically + if (wantedWidth <= currentWidth) return; + + const newWidth = Math.min(wantedWidth, 900); + + const header = document.querySelector( + `.grid-header[data-index="${columnIndex}"]`, + ); + + if (header) { + header.style.flex = `0 0 ${newWidth}px`; + } + + const topCell = document.querySelector( + `.grid-top .grid-cell:nth-child(${columnIndex + 1})`, + ); + + if (topCell) { + topCell.style.flex = `0 0 ${newWidth}px`; + } + + const cells = document.querySelectorAll( + `.grid-row .grid-cell[data-index="${columnIndex}"]`, + ); + + cells.forEach((c) => { + c.style.flex = `0 0 ${newWidth}px`; + }); + + const colPos = columnIndex - 1; + + if (columns[colPos]) { + columns[colPos].width = newWidth; + } + } function syncVisibleRowsToGridData() { if (!rowContainer) return; @@ -1127,6 +1205,10 @@ const cell = e.target.closest(".grid-cell"); if (!cell || !cell.dataset.row) return; + if (e.target.classList.contains("cell-input")) { + autoExpandColumnFromInput(e.target); + } + const rowIndex = parseInt(cell.dataset.row, 10); const colType = cell.dataset.colType; @@ -1138,6 +1220,16 @@ } }); + rowContainer.addEventListener("focusin", function (e) { + if (!e.target.classList.contains("cell-input")) return; + + autoExpandColumnFromInput(e.target); + + setTimeout(() => { + autoExpandColumnFromInput(e.target); + }, 50); + }); + // Persist tested_component before clicking + document.addEventListener("mousedown", function (e) { const btn = e.target.closest(".add-part-btn");