fixed column parts
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row parts-row">
|
||||
<div class="col-md-9">
|
||||
<div class="col-md-12">
|
||||
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
||||
<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>
|
||||
@@ -55,7 +55,7 @@
|
||||
<i class="fas fa-microphone"></i> Voce
|
||||
</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;">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
@@ -119,7 +119,7 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-3 d-none">
|
||||
<h6>Foto del Campione</h6>
|
||||
<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>
|
||||
@@ -432,7 +432,7 @@
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -611,7 +611,7 @@
|
||||
|
||||
#partsTable {
|
||||
table-layout: fixed !important;
|
||||
width: max-content !important;
|
||||
width: 100% !important;
|
||||
min-width: unset !important;
|
||||
}
|
||||
|
||||
@@ -733,9 +733,6 @@
|
||||
|
||||
/* Extra field propagation header - full column width */
|
||||
#partsTable th.extra-field-th {
|
||||
width: auto !important;
|
||||
min-width: 220px !important;
|
||||
max-width: none !important;
|
||||
padding: 0.25rem !important;
|
||||
}
|
||||
|
||||
@@ -757,9 +754,9 @@
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
color: #222;
|
||||
white-space: nowrap !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: unset !important;
|
||||
white-space: normal !important;
|
||||
word-break: break-word;
|
||||
overflow: hidden !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -806,5 +803,69 @@
|
||||
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 */
|
||||
</style>
|
||||
+128
-79
@@ -147,10 +147,9 @@ $(document).ready(function () {
|
||||
return `
|
||||
<td class="extra-field-td" style="width:200px;">
|
||||
<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>`;
|
||||
}
|
||||
|
||||
function buildExtraFieldHeaderHtml() {
|
||||
if (!partsExtraField) return "";
|
||||
|
||||
@@ -888,6 +887,17 @@ $(document).ready(function () {
|
||||
$("#partsTableBody").empty();
|
||||
$("#global-matrice").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();
|
||||
$(".modal-backdrop").remove();
|
||||
$("body").removeClass("modal-open").css("padding-right", "");
|
||||
@@ -2154,6 +2164,9 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
window.saveAllParts = saveAllParts;
|
||||
window.markUnsaved = function () {
|
||||
markUnsaved();
|
||||
};
|
||||
function renumberParts() {
|
||||
console.log(
|
||||
"Inizio rinumera parts, numero righe:",
|
||||
@@ -2672,135 +2685,171 @@ $(document).on("click", "#showHideImageBtn", function () {
|
||||
// RESIZABLE PARTS TABLE COLUMNS - FIXED COLGROUP VERSION
|
||||
// ===================
|
||||
(function () {
|
||||
// Larghezze default per indice colonna (0-based)
|
||||
const defaultWidths = [55, 320, 360, 150, 220, 230];
|
||||
const savedWidths = [...defaultWidths];
|
||||
const NUM_W = 55; // colonna Num fissa
|
||||
const MIN_W = 70; // larghezza minima di ogni colonna
|
||||
|
||||
// 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() {
|
||||
return $("#partsTableColgroup");
|
||||
}
|
||||
|
||||
function syncColgroupToHeaders() {
|
||||
const $table = $("#partsTable");
|
||||
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();
|
||||
function getThs() {
|
||||
return $("#partsTable thead tr:first th");
|
||||
}
|
||||
|
||||
// Applica le larghezze salvate
|
||||
$colgroup.find("col").each(function (i) {
|
||||
const w = savedWidths[i] !== undefined ? savedWidths[i] : 150;
|
||||
$(this).css("width", w + "px");
|
||||
function getAvail() {
|
||||
const w = $("#partsModal .parts-table-scroll").innerWidth();
|
||||
return w > 0 ? Math.floor(w) : 0;
|
||||
}
|
||||
|
||||
function ensureCols(n) {
|
||||
const $cg = getColgroup();
|
||||
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;
|
||||
}
|
||||
|
||||
function render() {
|
||||
const n = getThs().length;
|
||||
if (!n || !getAvail()) return;
|
||||
ensureCols(n);
|
||||
ensureWeights(n);
|
||||
const px = computePx(n);
|
||||
getColgroup()
|
||||
.find("col")
|
||||
.each(function (i) {
|
||||
$(this).css("width", px[i] + "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) {
|
||||
const w = Math.max(40, Math.round(newWidth));
|
||||
savedWidths[colIndex] = w;
|
||||
|
||||
const $col = getColgroup().find("col").eq(colIndex);
|
||||
if ($col.length) {
|
||||
$col.css("width", w + "px");
|
||||
}
|
||||
|
||||
// Aggiorna larghezza totale tabella senza toccare le altre colonne
|
||||
const thCount = $("#partsTable thead tr:first th").length;
|
||||
const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0);
|
||||
$("#partsTable").css("width", total + "px");
|
||||
}
|
||||
|
||||
function addResizers() {
|
||||
const $table = $("#partsTable");
|
||||
if (!$table.length) return;
|
||||
const $ths = getThs();
|
||||
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);
|
||||
|
||||
// Salta colonna Num (indice 0) — non ridimensionabile
|
||||
if (colIndex === 0) return;
|
||||
|
||||
// Non aggiungere due volte
|
||||
if ($th.find(".parts-resizer").length) return;
|
||||
|
||||
$th.css("position", "relative");
|
||||
const $resizer = $("<span class='parts-resizer'></span>");
|
||||
$th.css("position", "relative"); // necessario per il posizionamento assoluto
|
||||
$th.append($resizer);
|
||||
|
||||
$resizer.on("mousedown", function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
ensureWeights(n);
|
||||
const startPx = computePx(n);
|
||||
const a = startPx[i];
|
||||
const b = startPx[i + 1];
|
||||
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")
|
||||
.css("user-select", "none")
|
||||
.css("cursor", "col-resize");
|
||||
$("body").css({ "user-select": "none", cursor: "col-resize" });
|
||||
|
||||
$(document).on("mousemove.partsResize", function (ev) {
|
||||
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 () {
|
||||
$("body").css("user-select", "").css("cursor", "");
|
||||
$("body").css({ "user-select": "", cursor: "" });
|
||||
$(document).off(".partsResize");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
syncColgroupToHeaders();
|
||||
function refresh() {
|
||||
render();
|
||||
addResizers();
|
||||
}
|
||||
|
||||
// Init al primo show del modale
|
||||
$(document).on("shown.bs.modal", "#partsModal", function () {
|
||||
setTimeout(init, 120);
|
||||
setTimeout(refresh, 120);
|
||||
});
|
||||
|
||||
// Re-init dopo ogni AJAX (nuove righe, caricamenti)
|
||||
$(document).ajaxComplete(function () {
|
||||
if ($("#partsModal").hasClass("show")) {
|
||||
setTimeout(syncColgroupToHeaders, 200);
|
||||
setTimeout(addResizers, 220);
|
||||
setTimeout(refresh, 200);
|
||||
}
|
||||
});
|
||||
|
||||
// Re-init dopo aggiunta/rimozione righe
|
||||
$(document).on(
|
||||
"click",
|
||||
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, #renumberPartsBtn, #clonePartsBtn",
|
||||
function () {
|
||||
setTimeout(syncColgroupToHeaders, 120);
|
||||
setTimeout(addResizers, 140);
|
||||
setTimeout(refresh, 120);
|
||||
},
|
||||
);
|
||||
|
||||
window.initPartsResizableColumns = init;
|
||||
window.applyPartsColumnWidths = syncColgroupToHeaders;
|
||||
let resizeTimer = null;
|
||||
$(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
|
||||
|
||||
Reference in New Issue
Block a user