class ColumnManager { constructor(tableId, options = {}) { this.tableId = tableId; this.table = document.getElementById(tableId); if (!this.table) return; this.options = Object.assign({ entityType: 'individui', storageKey: null, resizable: true, reorderable: true, persistWidths: true, persistOrder: true, }, options); this.columns = []; this.columnWidths = {}; this.columnOrder = []; this.dragState = null; this.resizeState = null; this.init(); } init() { this.loadState(); this.initColumns(); if (this.options.resizable) this.initResize(); if (this.options.reorderable) this.initReorder(); this.applyState(); } initColumns() { const ths = this.table.querySelectorAll('thead th'); ths.forEach((th, idx) => { const colKey = th.dataset.column || `col_${idx}`; const col = { index: idx, key: colKey, element: th, width: th.style.width || '', visible: th.style.display !== 'none', label: th.textContent.trim(), }; this.columns.push(col); }); } loadState() { if (this.options.storageKey) { try { const saved = sessionStorage.getItem(this.options.storageKey); if (saved) { const data = JSON.parse(saved); this.columnWidths = data.widths || {}; this.columnOrder = data.order || []; return; } } catch (e) {} } const vistaEl = document.getElementById('vista-data'); if (vistaEl && vistaEl.textContent && vistaEl.textContent !== 'null') { try { const vista = JSON.parse(vistaEl.textContent); this.columnWidths = vista.colonne_larghezze || {}; this.columnOrder = vista.colonne_ordine || []; } catch (e) {} } } persistState() { if (!this.options.storageKey) return; try { sessionStorage.setItem(this.options.storageKey, JSON.stringify({ widths: this.columnWidths, order: this.columnOrder, })); } catch (e) {} } applyState() { if (this.columnOrder.length > 0) { this.reorderColumns(this.columnOrder, false); } if (Object.keys(this.columnWidths).length > 0) { this.applyWidths(); } } applyWidths() { this.columns.forEach(col => { const w = this.columnWidths[col.key]; if (w) { col.element.style.width = w; } }); } initResize() { this.table.querySelectorAll('thead th').forEach(th => { const handle = document.createElement('div'); handle.className = 'col-resize-handle'; handle.style.cssText = 'position:absolute;right:0;top:0;bottom:0;width:5px;cursor:col-resize;z-index:10;'; th.style.position = 'relative'; th.appendChild(handle); handle.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation(); const thEl = th; const startX = e.clientX; const startWidth = thEl.offsetWidth; const onMouseMove = (ev) => { const diff = ev.clientX - startX; const newWidth = Math.max(30, startWidth + diff); thEl.style.width = newWidth + 'px'; const colKey = thEl.dataset.column; if (colKey) { this.columnWidths[colKey] = newWidth + 'px'; this.persistState(); } }; const onMouseUp = () => { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); document.body.style.cursor = ''; document.body.style.userSelect = ''; }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; }); }); } initReorder() { const thead = this.table.querySelector('thead'); if (!thead) return; let dragCol = null; let dragIndex = -1; let placeholder = null; thead.querySelectorAll('th').forEach(th => { if (th.querySelector('.col-resize-handle')) return; th.draggable = true; th.addEventListener('dragstart', (e) => { dragCol = th; dragIndex = Array.from(thead.querySelectorAll('th')).indexOf(th); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', ''); th.classList.add('dragging'); }); th.addEventListener('dragend', () => { th.classList.remove('dragging'); if (placeholder && placeholder.parentNode) { placeholder.parentNode.removeChild(placeholder); } dragCol = null; dragIndex = -1; placeholder = null; }); th.addEventListener('dragover', (e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; if (!dragCol || dragCol === th) return; if (!placeholder) { placeholder = document.createElement('th'); placeholder.style.cssText = 'border:2px dashed #007bff;background:rgba(0,123,255,0.1);width:0;padding:0;'; dragCol.parentNode.insertBefore(placeholder, dragCol); } const rect = th.getBoundingClientRect(); const midX = rect.left + rect.width / 2; const parent = th.parentNode; if (e.clientX < midX) { parent.insertBefore(placeholder, th); } else { parent.insertBefore(placeholder, th.nextSibling); } }); th.addEventListener('drop', (e) => { e.preventDefault(); if (!dragCol || dragCol === th) return; const parent = th.parentNode; const allTh = Array.from(parent.querySelectorAll('th')).filter(el => el !== placeholder); const fromIdx = allTh.indexOf(dragCol); let toIdx = allTh.indexOf(th); if (placeholder) { toIdx = Array.from(parent.children).indexOf(placeholder); if (placeholder.parentNode) placeholder.parentNode.removeChild(placeholder); placeholder = null; } if (fromIdx < 0) return; const actualRows = this.table.querySelectorAll('tr'); actualRows.forEach(row => { const cells = row.querySelectorAll('td, th'); if (cells[fromIdx] && cells[toIdx]) { if (toIdx > fromIdx) { row.insertBefore(cells[fromIdx], cells[toIdx].nextSibling); } else { row.insertBefore(cells[fromIdx], cells[toIdx]); } } }); this.updateColumnOrder(); }); }); } updateColumnOrder() { const ths = this.table.querySelectorAll('thead th'); this.columnOrder = []; const keysInOrder = []; ths.forEach((th, idx) => { const colKey = th.dataset.column || `col_${idx}`; keysInOrder.push(colKey); this.columnOrder.push(colKey); }); this.columns.sort((a, b) => keysInOrder.indexOf(a.key) - keysInOrder.indexOf(b.key)); this.columns.forEach((col, idx) => col.index = idx); this.persistState(); } reorderColumns(keyOrder, persist = true) { if (!keyOrder || keyOrder.length === 0) return; const thead = this.table.querySelector('thead'); if (!thead) return; const rows = this.table.querySelectorAll('tr'); const keyToThIndex = {}; const allTh = Array.from(thead.querySelectorAll('th')); allTh.forEach((th, idx) => { const key = th.dataset.column || `col_${idx}`; keyToThIndex[key] = idx; }); rows.forEach(row => { const cells = row.querySelectorAll('td, th'); const reordered = []; let lastRef = null; keyOrder.forEach(key => { const srcIdx = keyToThIndex[key]; if (srcIdx !== undefined && cells[srcIdx]) { reordered.push(cells[srcIdx]); } }); reordered.forEach(cell => { if (lastRef) { row.insertBefore(cell, lastRef.nextSibling); } else { row.insertBefore(cell, row.firstChild); } lastRef = cell; }); }); this.columnOrder = [...keyOrder]; this.columns.sort((a, b) => keyOrder.indexOf(a.key) - keyOrder.indexOf(b.key)); this.columns.forEach((col, idx) => col.index = idx); if (persist) this.persistState(); } setVisible(columnKey, visible) { const th = this.table.querySelector(`th[data-column="${columnKey}"]`); if (th) th.style.display = visible ? '' : 'none'; const colIdx = this.columns.findIndex(c => c.key === columnKey); if (colIdx >= 0) { this.columns[colIdx].visible = visible; const rows = this.table.querySelectorAll('tbody tr, thead tr'); rows.forEach(row => { const cells = row.querySelectorAll('td, th'); if (cells[colIdx]) { cells[colIdx].style.display = visible ? '' : 'none'; } }); } } getColumnWidths() { return { ...this.columnWidths }; } getColumnOrder() { return [...this.columnOrder]; } getVisibleColumns() { return this.columns.filter(c => c.visible).map(c => c.key); } getColumnMetadata() { return { larghezze: this.getColumnWidths(), ordine: this.getColumnOrder(), visibili: this.getVisibleColumns(), }; } printWithSettings() { const printWindow = window.open('', '_blank'); if (!printWindow) return; const styles = Array.from(document.styleSheets) .filter(sheet => { try { return sheet.cssRules; } catch (e) { return false; } }) .map(sheet => Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n')) .join('\n'); const printStyles = ` @media print { body { font-family: 'DejaVu Sans', Arial, sans-serif; font-size: 10pt; } .no-print { display: none !important; } .card { border: none !important; box-shadow: none !important; } .table { border-collapse: collapse; width: 100%; } .table th { background: #f5f5f5 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; } .table th, .table td { border: 1px solid #ddd; padding: 4px 6px; } a { text-decoration: none; color: #000; } .badge { border: 1px solid #999; padding: 1px 4px; font-size: 8pt; } .pagination, .card-header .card-tools { display: none !important; } .page-break { page-break-before: always; } } `; const title = document.title || 'Stampa'; const pageTitle = document.querySelector('.page_title, .card-title h1, h1')?.textContent?.trim() || title; let content = ''; const printArea = this.table?.cloneNode(true); if (printArea) { printArea.querySelectorAll('.col-resize-handle').forEach(h => h.remove()); content = printArea.outerHTML; } else { content = document.getElementById(this.tableId)?.outerHTML || ''; } printWindow.document.write(` ${title}

${pageTitle}

${content}

Stampato il ${new Date().toLocaleDateString('it-IT')} — ${document.location.href}

`); printWindow.document.close(); } } window.ColumnManager = ColumnManager;