Optimize table view and export for software lists added. excelexport fixed - first line

This commit is contained in:
2026-09-24 11:38:40 +02:00
parent 80f6136399
commit 8de15f223c
16 changed files with 320 additions and 24 deletions
+81
View File
@@ -3241,3 +3241,84 @@ html[data-theme="dark"] .advanced-filter-popup select,html[data-theme="dark"] .a
.software-package-start-option{display:inline-flex!important;flex-direction:row!important;align-items:center!important;justify-content:flex-start!important;gap:.55rem!important;width:auto!important;max-width:100%!important;margin:0!important;}
.software-package-start-application input[type=checkbox],
.software-package-start-option input[type=checkbox]{width:auto!important;flex:0 0 auto!important;margin:0!important;}
/* v0.5.5.87+: one table implementation across all interactive data lists.
table-tools.js adds standard-data-table-scroll automatically, including to
older views that did not have an explicit table-scroll wrapper. */
.standard-data-table-scroll{
width:100%;
max-width:100%;
max-height:calc(100vh - 230px);
min-height:0;
overflow:auto;
overscroll-behavior:contain;
border:1px solid var(--am-border,#d8dde2);
border-radius:6px;
background:var(--am-surface,#fff);
}
.standard-data-table-scroll > .data-table{
margin:0;
}
/* v0.5.5.88 correction: keep the complete THEAD sticky.
The TH cells stay position:relative so resize handles keep the correct
positioning context. This combines the sticky behavior that already
worked in 0.5.5.86 with the unified resize logic from 0.5.5.87. */
.standard-data-table-scroll > .data-table > thead{
position:sticky !important;
top:0 !important;
z-index:32;
/* Opaque background closes the tiny collapsed-border seam between the
title row and filter row so scrolled table content cannot shine through. */
background:#f5f7f9;
}
html[data-theme="dark"] .standard-data-table-scroll > .data-table > thead{
background:#232e39;
}
/* The THEAD owns vertical sticking. Individual header cells must therefore
not be sticky themselves, but they must remain positioned for resizers. */
.standard-data-table-scroll > .data-table > thead > tr > th{
position:relative !important;
top:auto !important;
z-index:auto;
overflow:visible !important;
background-clip:padding-box;
}
.standard-data-table-scroll > .data-table > thead > tr:first-child > th{
background:#e7ecf0;
}
.standard-data-table-scroll > .data-table > thead > .column-filter-row > th{
background:#f5f7f9;
}
.standard-data-table-scroll > .data-table > thead > .column-filter-row > th.filter-cell-active{
background:#fff4c2;
}
html[data-theme="dark"] .standard-data-table-scroll > .data-table > thead > tr:first-child > th{
background:#293541;
}
html[data-theme="dark"] .standard-data-table-scroll > .data-table > thead > .column-filter-row > th{
background:#232e39;
}
html[data-theme="dark"] .standard-data-table-scroll > .data-table > thead > .column-filter-row > th.filter-cell-active{
background:#554612;
}
/* Sticky THEAD and resize handles must coexist: every resizable header remains
the absolute-positioning context for its handle. */
.standard-data-table th.table-resizable-column-header,
.standard-data-table th.table-row-number-header{
position:relative !important;
overflow:visible !important;
}
.standard-data-table th.table-resizable-column-header > .table-column-resizer,
.standard-data-table th.table-row-number-header > .table-row-number-resizer{
position:absolute !important;
top:0 !important;
right:0 !important;
bottom:0 !important;
width:10px !important;
z-index:40 !important;
}
+68 -6
View File
@@ -165,14 +165,69 @@
return collator.compare(String(left.value), String(right.value));
}
function ensureStandardTableShell(table) {
table.classList.add('standard-data-table');
const shellSelector = '.standard-data-table-scroll, .table-scroll, .table-wrap, .sticky-table, .management-table-scroll';
const directParent = table.parentElement;
let shell = directParent?.matches(shellSelector) ? directParent : null;
if (!shell) {
const parent = table.parentNode;
if (!parent) return null;
shell = document.createElement('div');
shell.className = 'table-scroll standard-data-table-scroll';
parent.insertBefore(shell, table);
shell.appendChild(table);
} else {
shell.classList.add('standard-data-table-scroll');
}
return shell;
}
function ensureStableTableStorageKey(table) {
if (table.dataset.storageKey) return table.dataset.storageKey;
if (table.id) {
table.dataset.storageKey = table.id;
return table.id;
}
const tables = [...document.querySelectorAll('main table.data-table')];
const index = Math.max(0, tables.indexOf(table));
const key = `${window.location.pathname}:data-table-${index}`;
table.dataset.storageKey = key;
return key;
}
function initTable(table) {
const thead = table.tHead;
const tbody = table.tBodies[0];
if (!thead || !tbody || !thead.rows.length) return;
const tableShell = ensureStandardTableShell(table);
ensureStableTableStorageKey(table);
if (table.dataset.tableToolsReady === '1') return;
const headerRow = thead.rows[0];
// Sticky title/filter rows and resizable columns must coexist. Sticky is
// applied to the TH cells in CSS; this custom property supplies the exact
// offset for the filter row from the rendered title-row height.
function updateStickyHeaderMetrics() {
const measured = headerRow.getBoundingClientRect().height || headerRow.offsetHeight || 0;
if (measured > 0) {
table.style.setProperty('--table-title-row-height', `${Math.ceil(measured)}px`);
}
}
if (typeof ResizeObserver === 'function') {
const observer = new ResizeObserver(() => updateStickyHeaderMetrics());
observer.observe(headerRow);
table._stickyHeaderResizeObserver = observer;
}
window.addEventListener('resize', updateStickyHeaderMetrics);
// Add a purely visual row-number column before every real table column.
// It is recalculated after filtering, sorting and partial server reloads and
// therefore never represents a database identifier.
@@ -188,7 +243,7 @@
}
const numberHeader = headerRow.querySelector('th[data-row-number-column="1"]');
const rowNumberStoragePart = table.dataset.storageKey || table.id || window.location.pathname;
const rowNumberStoragePart = ensureStableTableStorageKey(table);
const rowNumberWidthKey = `table:${rowNumberStoragePart}:row-number-width`;
const defaultRowNumberWidth = 64;
const minimumRowNumberWidth = 48;
@@ -1189,10 +1244,15 @@
});
thead.appendChild(filterRow);
if (isAssetTable) {
installColumnResizers();
applyStoredColumnWidths();
}
updateStickyHeaderMetrics();
requestAnimationFrame(updateStickyHeaderMetrics);
// All AssetManager data tables use the same resize implementation.
// This keeps width changes, table growth and user-state persistence
// identical across Assets, Software, Jobs, Settings and detail lists.
installColumnResizers();
applyStoredColumnWidths();
requestAnimationFrame(updateStickyHeaderMetrics);
// Editable cells are always read live, so changed values immediately
// participate in filtering without a stale cache.
@@ -1210,6 +1270,7 @@
table.assetUpdateRowNumbers = updateRowNumbers;
table.assetRebuildFilterCache = rebuildMissingCellKeys;
table.assetApplyStoredColumnWidths = applyStoredColumnWidths;
table.assetUpdateStickyHeaderMetrics = updateStickyHeaderMetrics;
table.dataset.tableToolsReady = '1';
ensureFilterStatus();
@@ -1352,7 +1413,7 @@
const initializeTableTools = () => {
document.querySelectorAll('table.data-table').forEach(initTable);
document.querySelectorAll('main table:not(#asset-table):not([data-column-resize="off"])').forEach(initUniversalTableResize);
document.querySelectorAll('main table:not(.data-table):not([data-column-resize="off"])').forEach(initUniversalTableResize);
};
if (uiState?.ready) uiState.ready.finally(initializeTableTools);
else initializeTableTools();
@@ -1369,6 +1430,7 @@
}
table.assetUpdateRowNumbers?.();
table.assetApplyStoredColumnWidths?.();
requestAnimationFrame(() => table.assetUpdateStickyHeaderMetrics?.());
}
});
})();