PhysInOne-Leaderboard / src /display /table_layout.py
HAHAHA97's picture
UI update
a78e69d
Raw History Blame Contribute Delete
10.1 kB
"""Initial widths and spreadsheet-style resizing for submission tables."""
def column_width(column: str) -> int:
if column.startswith(("Young's Modulus (", "Poisson's Ratio (", "Fluid Viscosity (",
"Shear Modulus (", "Bulk Modulus (", "Yield Stress (",
"Plasticity Viscosity (", "Friction Angle (", "Initial Velocity (")):
return 220
widths = {
'#': 76,
'ID': 132,
'Submission': 132,
'Status': 132,
'Submitted At': 232,
'Completed At': 232,
'Updated At': 232,
'Team': 280,
'Submitter': 280,
'Method / Model': 280,
'Owner': 240,
'Task': 180,
'Position': 100,
'Scenes': 176,
'Score': 144,
'Error': 320,
'Scene Errors': 320,
'Links': 120,
}
return widths.get(column, max(112, min(220, len(column) * 7 + 44)))
table_resize_js = r"""() => {
if (window.physTableResizeInstalled) return;
window.physTableResizeInstalled = true;
const widths = new WeakMap();
const minimumFor = name => name === '#' ? 76 :
name === 'Team' || name === 'Submitter' || name === 'Method / Model' ? 130 :
name === 'Submitted At' || name === 'Updated At' || name === 'Completed At' ? 175 :
name === 'ID' || name === 'Submission' ? 120 :
name === 'Error' || name === 'Scene Errors' ? 140 : 100;
const initialWidthFor = name => /^(Young's Modulus|Poisson's Ratio|Fluid Viscosity|Shear Modulus|Bulk Modulus|Yield Stress|Plasticity Viscosity|Friction Angle|Initial Velocity) \(/.test(name) ? 220 : ({
'#': 76, ID: 132, Submission: 132, Status: 132,
'Submitted At': 232, 'Completed At': 232, 'Updated At': 232,
Team: 280, Submitter: 280, 'Method / Model': 280, Owner: 240, Task: 180, Position: 100, Scenes: 176,
Score: 144, Error: 320, 'Scene Errors': 320, Links: 120
})[name] || Math.max(112, Math.min(220, name.length * 7 + 44));
const formatMetric = value => {
const number = '[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?';
if (new RegExp(`^${number}$`).test(value) && Number.isFinite(Number(value))) {
return Number(value).toFixed(3);
}
const pair = value.match(new RegExp(`^(${number})\\s*([+±])\\s*(${number})$`));
return pair && Number.isFinite(Number(pair[1])) && Number.isFinite(Number(pair[3])) ?
`${Number(pair[1]).toFixed(3)}${pair[2]}${Number(pair[3]).toFixed(3)}` : value;
};
const applyWidths = (table, headers, saved) => {
let total = 0;
headers.forEach((header, index) => {
const width = saved.get(index);
total += width;
const property = `--cell-width-${index}`;
if (table.style.getPropertyValue(property) !== `${width}px`) {
table.style.setProperty(property, `${width}px`);
}
header.querySelector('.column-resize-handle')?.setAttribute('aria-valuenow', Math.round(width));
});
if (table.style.getPropertyValue('--table-content-width') !== `${total}px`) {
table.style.setProperty('--table-content-width', `${total}px`);
}
};
const install = () => {
document.querySelectorAll('.data-table').forEach(component => {
if (component.classList.contains('history-table')) {
const table = component.querySelector('table:not([aria-hidden="true"])');
if (table && component.getBoundingClientRect().height > 0) {
component.classList.toggle('is-empty', !table.tBodies[0]?.rows.length);
}
}
component.querySelectorAll('table:not([aria-hidden="true"])').forEach(table => {
const headers = Array.from(table.querySelectorAll('thead th:not(.row-number)'));
if (!headers.length) return;
if (!widths.has(component)) widths.set(component, new Map());
const saved = widths.get(component);
headers.forEach((header, index) => {
const name = header.dataset.columnName || header.getAttribute('title') || header.textContent.trim();
header.dataset.columnName = name;
const metric = name.match(/\b(PMF|PSNR|SSIM|LPIPS|FVD)\b/i)?.[1]?.toUpperCase();
const parameter = /^(N|NN|E|P|S) /.test(name) ||
Boolean(table.closest('.property-material-table') &&
!['Completed At', 'ID', 'Team', 'Submitter', 'Method / Model', 'Task', 'Status', 'Links', 'Scenes', 'Submission'].includes(name));
const kind = metric || parameter ? 'metric' : name === 'Links' ? 'link-action' : ['Team', 'Submitter', 'Method / Model'].includes(name) ? 'model' :
['Updated At', 'Submitted At', 'Completed At', 'Submission', 'ID', 'Owner', 'Task'].includes(name) ? 'metadata' : '';
header.dataset.columnKind = kind;
if ((metric || parameter) && !header.querySelector('.metric-direction')) {
const direction = ['LPIPS', 'FVD'].includes(metric) || parameter ? '↓' : '↑';
const badge = document.createElement('span');
badge.className = 'metric-direction';
badge.textContent = direction;
header.querySelector('.header-content')?.appendChild(badge);
header.title = `${name}: ${direction === '↑' ? 'higher' : 'lower'} is better`;
}
table.querySelectorAll(`tbody td[data-col="${index}"]`).forEach(cell => {
cell.dataset.columnKind = kind;
if (kind === 'metric' || name === 'Score') {
const walker = document.createTreeWalker(cell, NodeFilter.SHOW_TEXT);
let node;
while ((node = walker.nextNode())) {
const original = node.textContent.trim();
const formatted = formatMetric(original);
if (formatted !== original) {
cell.title = original;
node.textContent = formatted;
}
}
}
if (name === '#' && component.classList.contains('leaderboard-table')) {
cell.dataset.rank = cell.textContent.trim();
}
});
if (!saved.has(index)) {
saved.set(index, Math.max(minimumFor(name), initialWidthFor(name)));
}
if (header.querySelector('.column-resize-handle')) return;
const setWidth = requested => {
const width = Math.max(minimumFor(name), Math.round(requested));
if (width === saved.get(index)) return;
saved.set(index, width);
applyWidths(table, headers, saved);
};
const handle = document.createElement('span');
handle.className = 'column-resize-handle resize-right';
handle.tabIndex = 0;
handle.setAttribute('role', 'separator');
handle.setAttribute('aria-orientation', 'vertical');
handle.setAttribute('aria-label', `Resize ${name} column (right edge)`);
handle.setAttribute('aria-valuemin', minimumFor(name));
handle.title = 'Drag to resize column; use arrow keys when focused';
handle.addEventListener('click', event => event.stopPropagation());
handle.addEventListener('pointerdown', event => {
if (event.button !== 0) return;
event.preventDefault();
event.stopPropagation();
const start = event.clientX;
const size = saved.get(index);
handle.setPointerCapture(event.pointerId);
handle.classList.add('resizing');
handle.onpointermove = move => {
move.preventDefault();
move.stopPropagation();
setWidth(size + move.clientX - start);
};
const finish = () => {
handle.onpointermove = null;
handle.classList.remove('resizing');
if (handle.hasPointerCapture(event.pointerId)) handle.releasePointerCapture(event.pointerId);
};
handle.onpointerup = finish;
handle.onpointercancel = finish;
handle.onlostpointercapture = finish;
});
handle.addEventListener('keydown', event => {
if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
event.preventDefault();
event.stopPropagation();
setWidth(saved.get(index) + (event.key === 'ArrowRight' ? 16 : -16));
});
header.appendChild(handle);
});
applyWidths(table, headers, saved);
});
});
};
let scheduled = false;
new MutationObserver(() => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(() => { scheduled = false; install(); });
}).observe(document.body, {
childList: true, characterData: true, subtree: true, attributes: true, attributeFilter: ['style']
});
install();
}"""