fixed column parts

This commit is contained in:
2026-10-05 13:38:42 +02:00
parent 65608712ad
commit 76f4a8e291
2 changed files with 198 additions and 88 deletions
+72 -11
View File
@@ -24,7 +24,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="row parts-row"> <div class="row parts-row">
<div class="col-md-9"> <div class="col-md-12">
<!-- Prima riga: Elenco Parti, Rinumera, Voce --> <!-- Prima riga: Elenco Parti, Rinumera, Voce -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6> <h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
@@ -55,7 +55,7 @@
<i class="fas fa-microphone"></i> Voce <i class="fas fa-microphone"></i> Voce
</button> </button>
<button type="button" class="btn btn-outline-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"> <button type="button" class="btn btn-outline-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i> <i class="fas fa-eye" style="font-size: 0.8rem;"></i>
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i> <i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
</button> </button>
</div> </div>
@@ -119,7 +119,7 @@
</table> </table>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3 d-none">
<h6>Foto del Campione</h6> <h6>Foto del Campione</h6>
<div style="display: flex; align-items: center; margin-bottom: 10px;"> <div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button> <button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
@@ -432,7 +432,7 @@
} }
/* Evita stretching del flex nella riga dei filtri */ /* Evita stretching del flex nella riga dei filtri */
#partsModal .modal-body>.row .col-md-9>div[style*="display: flex"]>* { #partsModal .parts-row>div:first-child>div[style*="display: flex"]>* {
flex: 0 0 auto; flex: 0 0 auto;
} }
@@ -611,7 +611,7 @@
#partsTable { #partsTable {
table-layout: fixed !important; table-layout: fixed !important;
width: max-content !important; width: 100% !important;
min-width: unset !important; min-width: unset !important;
} }
@@ -733,9 +733,6 @@
/* Extra field propagation header - full column width */ /* Extra field propagation header - full column width */
#partsTable th.extra-field-th { #partsTable th.extra-field-th {
width: auto !important;
min-width: 220px !important;
max-width: none !important;
padding: 0.25rem !important; padding: 0.25rem !important;
} }
@@ -757,9 +754,9 @@
font-weight: 700; font-weight: 700;
line-height: 1.15; line-height: 1.15;
color: #222; color: #222;
white-space: nowrap !important; white-space: normal !important;
overflow: visible !important; word-break: break-word;
text-overflow: unset !important; overflow: hidden !important;
text-align: center; text-align: center;
} }
@@ -806,5 +803,69 @@
z-index: 1065 !important z-index: 1065 !important
} }
/* Campi nelle celle: occupano tutta la cella */
#partsTable td input.form-control,
#partsTable td textarea.form-control {
width: 100% !important;
max-width: 100% !important;
}
#partsTable td>div[style*="display: flex"] {
width: 100%;
min-width: 0;
}
/* Select2 nelle celle (Matrice ed Extra): occupano la cella e vanno a capo */
#partsTable td .select2-container {
width: 100% !important;
min-width: 0 !important;
max-width: 100% !important;
}
#partsTable td .part-matrice+.select2 {
flex: 1 1 auto !important;
width: auto !important;
}
#partsTable td .select2-selection--single {
height: auto !important;
min-height: 24px !important;
display: flex !important;
align-items: center;
}
#partsTable td .select2-selection__rendered {
flex: 1 1 auto;
white-space: normal !important;
word-break: break-word;
overflow: visible !important;
text-overflow: clip !important;
line-height: 1.2 !important;
padding: 2px 22px 2px 4px !important;
}
#partsTable td .select2-selection__arrow {
height: 100% !important;
top: 0 !important;
}
/* Campo extra di tipo testo: textarea che cresce col contenuto */
#partsTable textarea.part-extra-field {
field-sizing: content;
min-height: 24px;
height: auto;
padding: 0.1rem 0.3rem;
line-height: 1.2;
font-size: 0.8rem;
resize: none;
overflow: hidden;
}
/* Voci del dropdown che vanno a capo */
.select2-results__option {
white-space: normal;
word-break: break-word;
}
/* rosso */ /* rosso */
</style> </style>
+126 -77
View File
@@ -147,10 +147,9 @@ $(document).ready(function () {
return ` return `
<td class="extra-field-td" style="width:200px;"> <td class="extra-field-td" style="width:200px;">
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}"> <input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
<input type="text" class="form-control form-control-sm part-extra-field" data-type="${partsExtraField.data_type || ""}" value="${selectedValueText || ""}"> <textarea rows="1" class="form-control form-control-sm part-extra-field" data-type="${partsExtraField.data_type || ""}">${selectedValueText || ""}</textarea>
</td>`; </td>`;
} }
function buildExtraFieldHeaderHtml() { function buildExtraFieldHeaderHtml() {
if (!partsExtraField) return ""; if (!partsExtraField) return "";
@@ -888,6 +887,17 @@ $(document).ready(function () {
$("#partsTableBody").empty(); $("#partsTableBody").empty();
$("#global-matrice").empty(); $("#global-matrice").empty();
$("#macro-matrice-filter").empty(); $("#macro-matrice-filter").empty();
// Ripristina lo stato di default: foto nascosta
const $partsRow = $("#partsModal .parts-row");
$partsRow.find("> .col-md-3").addClass("d-none");
$partsRow
.find("> div:first-child")
.removeClass("col-md-9")
.addClass("col-md-12");
$("#showHideImageBtn").html(
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
);
$(".temp-alert").remove(); $(".temp-alert").remove();
$(".modal-backdrop").remove(); $(".modal-backdrop").remove();
$("body").removeClass("modal-open").css("padding-right", ""); $("body").removeClass("modal-open").css("padding-right", "");
@@ -2154,6 +2164,9 @@ $(document).ready(function () {
}); });
window.saveAllParts = saveAllParts; window.saveAllParts = saveAllParts;
window.markUnsaved = function () {
markUnsaved();
};
function renumberParts() { function renumberParts() {
console.log( console.log(
"Inizio rinumera parts, numero righe:", "Inizio rinumera parts, numero righe:",
@@ -2672,135 +2685,171 @@ $(document).on("click", "#showHideImageBtn", function () {
// RESIZABLE PARTS TABLE COLUMNS - FIXED COLGROUP VERSION // RESIZABLE PARTS TABLE COLUMNS - FIXED COLGROUP VERSION
// =================== // ===================
(function () { (function () {
// Larghezze default per indice colonna (0-based) const NUM_W = 55; // colonna Num fissa
const defaultWidths = [55, 320, 360, 150, 220, 230]; const MIN_W = 70; // larghezza minima di ogni colonna
const savedWidths = [...defaultWidths];
// Pesi di default delle colonne dopo Num
// senza extra: Descrizione, Matrice, Data, Azioni
// con extra: Descrizione, Matrice, Data, Extra, Azioni
const DEFAULT_NO_EXTRA = [320, 360, 150, 200];
const DEFAULT_EXTRA = [280, 320, 140, 240, 180];
let weights = null;
let weightsCount = 0;
function getColgroup() { function getColgroup() {
return $("#partsTableColgroup"); return $("#partsTableColgroup");
} }
function syncColgroupToHeaders() { function getThs() {
const $table = $("#partsTable"); return $("#partsTable thead tr:first th");
const $ths = $table.find("thead tr:first th");
const $colgroup = getColgroup();
const thCount = $ths.length;
// Assicura che ci siano esattamente tante <col> quante <th>
while ($colgroup.find("col").length < thCount) {
$colgroup.append("<col>");
}
while ($colgroup.find("col").length > thCount) {
$colgroup.find("col:last").remove();
}
// Applica le larghezze salvate
$colgroup.find("col").each(function (i) {
const w = savedWidths[i] !== undefined ? savedWidths[i] : 150;
$(this).css("width", w + "px");
});
// Imposta larghezza totale della tabella = somma colonne (evita reflow)
const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0);
$table.css("width", total + "px");
} }
function applyColumnWidth(colIndex, newWidth) { function getAvail() {
const w = Math.max(40, Math.round(newWidth)); const w = $("#partsModal .parts-table-scroll").innerWidth();
savedWidths[colIndex] = w; return w > 0 ? Math.floor(w) : 0;
}
const $col = getColgroup().find("col").eq(colIndex); function ensureCols(n) {
if ($col.length) { const $cg = getColgroup();
$col.css("width", w + "px"); while ($cg.find("col").length < n) $cg.append("<col>");
while ($cg.find("col").length > n) $cg.find("col:last").remove();
}
function ensureWeights(n) {
const k = n - 1;
if (weights && weightsCount === k) return;
if (k === 4) weights = DEFAULT_NO_EXTRA.slice();
else if (k === 5) weights = DEFAULT_EXTRA.slice();
else weights = Array(k).fill(150);
weightsCount = k;
}
// Converte i pesi in pixel: la somma è sempre = larghezza disponibile
function computePx(n) {
const avail = getAvail();
const rest = Math.max(avail - NUM_W, MIN_W * (n - 1));
const sum = weights.reduce((a, b) => a + b, 0) || 1;
const px = [NUM_W];
let used = 0;
for (let i = 0; i < weights.length; i++) {
const v =
i === weights.length - 1
? rest - used
: Math.floor((rest * weights[i]) / sum);
px.push(v);
used += v;
} }
return px;
}
// Aggiorna larghezza totale tabella senza toccare le altre colonne function render() {
const thCount = $("#partsTable thead tr:first th").length; const n = getThs().length;
const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0); if (!n || !getAvail()) return;
$("#partsTable").css("width", total + "px"); ensureCols(n);
ensureWeights(n);
const px = computePx(n);
getColgroup()
.find("col")
.each(function (i) {
$(this).css("width", px[i] + "px");
});
} }
function addResizers() { function addResizers() {
const $table = $("#partsTable"); const $ths = getThs();
if (!$table.length) return; const n = $ths.length;
if (!n) return;
$ths.find(".parts-resizer").remove();
// Resizer sul bordo destro delle colonne da 1 a n-2:
// sposta spazio tra la colonna e quella alla sua destra
$ths.each(function (i) {
if (i === 0 || i >= n - 1) return;
$table.find("thead tr:first th").each(function (colIndex) {
const $th = $(this); const $th = $(this);
$th.css("position", "relative");
// Salta colonna Num (indice 0) — non ridimensionabile
if (colIndex === 0) return;
// Non aggiungere due volte
if ($th.find(".parts-resizer").length) return;
const $resizer = $("<span class='parts-resizer'></span>"); const $resizer = $("<span class='parts-resizer'></span>");
$th.css("position", "relative"); // necessario per il posizionamento assoluto
$th.append($resizer); $th.append($resizer);
$resizer.on("mousedown", function (e) { $resizer.on("mousedown", function (e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
ensureWeights(n);
const startPx = computePx(n);
const a = startPx[i];
const b = startPx[i + 1];
const startX = e.pageX; const startX = e.pageX;
// Leggi la larghezza ESATTA dalla <col>, non dal <th>
const startWidth =
savedWidths[colIndex] !== undefined
? savedWidths[colIndex]
: parseInt(
getColgroup()
.find("col")
.eq(colIndex)
.css("width"),
10,
) || 150;
$("body") $("body").css({ "user-select": "none", cursor: "col-resize" });
.css("user-select", "none")
.css("cursor", "col-resize");
$(document).on("mousemove.partsResize", function (ev) { $(document).on("mousemove.partsResize", function (ev) {
const delta = ev.pageX - startX; const delta = ev.pageX - startX;
applyColumnWidth(colIndex, startWidth + delta); const newA = Math.min(
Math.max(a + delta, MIN_W),
a + b - MIN_W,
);
const newB = a + b - newA;
const px = startPx.slice();
px[i] = newA;
px[i + 1] = newB;
weights = px.slice(1); // i pixel diventano i nuovi pesi
render();
}); });
$(document).on("mouseup.partsResize", function () { $(document).on("mouseup.partsResize", function () {
$("body").css("user-select", "").css("cursor", ""); $("body").css({ "user-select": "", cursor: "" });
$(document).off(".partsResize"); $(document).off(".partsResize");
}); });
}); });
}); });
} }
function init() { function refresh() {
syncColgroupToHeaders(); render();
addResizers(); addResizers();
} }
// Init al primo show del modale
$(document).on("shown.bs.modal", "#partsModal", function () { $(document).on("shown.bs.modal", "#partsModal", function () {
setTimeout(init, 120); setTimeout(refresh, 120);
}); });
// Re-init dopo ogni AJAX (nuove righe, caricamenti)
$(document).ajaxComplete(function () { $(document).ajaxComplete(function () {
if ($("#partsModal").hasClass("show")) { if ($("#partsModal").hasClass("show")) {
setTimeout(syncColgroupToHeaders, 200); setTimeout(refresh, 200);
setTimeout(addResizers, 220);
} }
}); });
// Re-init dopo aggiunta/rimozione righe
$(document).on( $(document).on(
"click", "click",
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, #renumberPartsBtn, #clonePartsBtn", ".add-row-global, .add-mix-global, .add-mix-row, .remove-row, #renumberPartsBtn, #clonePartsBtn",
function () { function () {
setTimeout(syncColgroupToHeaders, 120); setTimeout(refresh, 120);
setTimeout(addResizers, 140);
}, },
); );
window.initPartsResizableColumns = init; let resizeTimer = null;
window.applyPartsColumnWidths = syncColgroupToHeaders; $(window).on("resize.partsCols", function () {
if (!$("#partsModal").hasClass("show")) return;
clearTimeout(resizeTimer);
resizeTimer = setTimeout(render, 150);
});
// Textarea campo extra: auto-altezza (per browser senza field-sizing)
$(document).on(
"input focus",
"#partsTable textarea.part-extra-field",
function () {
this.style.height = "auto";
this.style.height = this.scrollHeight + "px";
},
);
window.initPartsResizableColumns = refresh;
window.applyPartsColumnWidths = refresh;
})(); })();
// =================== // ===================
// NAVIGAZIONE TABELLA PARTI CON FRECCE TASTIERA // NAVIGAZIONE TABELLA PARTI CON FRECCE TASTIERA