Stabilize accumulator preview layout
This commit is contained in:
+17
-34
@@ -6,13 +6,14 @@ const NODE_NAME = "SxCPAccumulatorPreview";
|
||||
const STYLE_ID = "sxcp-accumulator-preview-styles";
|
||||
const DEBUG_STORAGE_KEY = "sxcpAccumulatorPreviewDebug";
|
||||
|
||||
const MIN_CELL_W = 180;
|
||||
const MIN_CELL_W = 150;
|
||||
const MAX_CELL_W = 190;
|
||||
const DEFAULT_GRID_H = 360;
|
||||
const GAP = 6;
|
||||
const PAD = 4;
|
||||
const TOOLBAR_H = 28;
|
||||
const CAPTION_H = 20;
|
||||
const EMPTY_GRID_H = 84;
|
||||
const MAX_GRID_H = 640;
|
||||
const MIN_W = 560;
|
||||
const MARGIN = 10;
|
||||
|
||||
@@ -225,13 +226,14 @@ function injectStyles() {
|
||||
flex:1 1 auto; min-height:0; box-sizing:border-box; }
|
||||
.sxap-grid.sxap-dragover { outline:2px dashed #6cf; outline-offset:-2px; }
|
||||
.sxap-empty { width:100%; padding:12px; text-align:center; font-size:12px; opacity:0.65; box-sizing:border-box; }
|
||||
.sxap-cell { position:relative; width:var(--sxap-cell-w, 240px); border:2px solid transparent;
|
||||
.sxap-cell { position:relative; width:var(--sxap-cell-w, 170px); border:2px solid transparent;
|
||||
border-radius:4px; overflow:hidden; background:#222; box-sizing:border-box;
|
||||
transition:border-color .1s, background .1s; }
|
||||
.sxap-cell:hover { border-color:#555; }
|
||||
.sxap-cell.sxap-selected { border-color:#6cf; }
|
||||
.sxap-cell.sxap-drop { border-color:#fc6; border-style:dashed; }
|
||||
.sxap-thumb { width:100%; object-fit:contain; display:block; cursor:grab; background:#111; }
|
||||
.sxap-thumb { width:100%; height:var(--sxap-thumb-h, 170px); object-fit:contain; display:block;
|
||||
cursor:grab; background:#111; }
|
||||
.sxap-thumb:active { cursor:grabbing; }
|
||||
.sxap-badge { position:absolute; top:2px; left:2px; font-size:10px; background:rgba(0,0,0,0.65);
|
||||
color:#fff; padding:0 4px; border-radius:3px; pointer-events:none; }
|
||||
@@ -255,38 +257,21 @@ function injectStyles() {
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function entryAspect(entry) {
|
||||
const shape = Array.isArray(entry?.shape) ? entry.shape : [];
|
||||
const height = Number(shape[0] || 0);
|
||||
const width = Number(shape[1] || 0);
|
||||
if (height > 0 && width > 0) return height / width;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function layoutMetrics(node) {
|
||||
const width = Math.max(node.size?.[0] || MIN_W, MIN_W);
|
||||
const inner = Math.max(MIN_CELL_W, width - 2 * MARGIN - 2 * PAD);
|
||||
const entries = imageEntries(node);
|
||||
const count = entries.length;
|
||||
const perRow = count <= 1 ? 1 : Math.max(1, Math.floor((inner + GAP) / (MIN_CELL_W + GAP)));
|
||||
const cellW = Math.max(MIN_CELL_W, Math.floor((inner - GAP * (perRow - 1)) / perRow));
|
||||
let gridH = EMPTY_GRID_H;
|
||||
if (count > 0) {
|
||||
gridH = 2 * PAD;
|
||||
for (let start = 0; start < count; start += perRow) {
|
||||
const row = entries.slice(start, start + perRow);
|
||||
const rowH = Math.max(...row.map((entry) => Math.round(cellW * entryAspect(entry)) + CAPTION_H));
|
||||
gridH += rowH;
|
||||
if (start + perRow < count) gridH += GAP;
|
||||
}
|
||||
}
|
||||
return {cellW, gridH};
|
||||
const perRow = Math.max(1, Math.floor((inner + GAP) / (MIN_CELL_W + GAP)));
|
||||
const rawCellW = Math.floor((inner - GAP * (perRow - 1)) / perRow);
|
||||
const cellW = Math.min(MAX_CELL_W, Math.max(MIN_CELL_W, rawCellW));
|
||||
const thumbH = Math.round(cellW * 1.1);
|
||||
return {cellW, thumbH, gridH: DEFAULT_GRID_H};
|
||||
}
|
||||
|
||||
function recomputeSize(node) {
|
||||
const {cellW, gridH} = layoutMetrics(node);
|
||||
const {cellW, thumbH, gridH} = layoutMetrics(node);
|
||||
node._sxapCellW = cellW;
|
||||
node._sxapWidgetH = 2 * MARGIN + TOOLBAR_H + 6 + Math.min(gridH, MAX_GRID_H);
|
||||
node._sxapThumbH = thumbH;
|
||||
node._sxapWidgetH = 2 * MARGIN + TOOLBAR_H + 6 + Math.max(gridH, EMPTY_GRID_H);
|
||||
}
|
||||
|
||||
function syncWidgetWidth(node) {
|
||||
@@ -298,11 +283,9 @@ function resizeToContent(node) {
|
||||
const targetH = node.computeSize?.()[1] || node._sxapWidgetH || 140;
|
||||
const width = node.size?.[0] || MIN_W;
|
||||
const currentH = node.size?.[1] || 0;
|
||||
const previousAutoH = node._sxapAutoHeight || 0;
|
||||
const userSizedTaller = previousAutoH > 0 && currentH > previousAutoH + 6 && currentH > targetH;
|
||||
const nextH = userSizedTaller ? currentH : targetH;
|
||||
const nextH = currentH > 0 ? Math.max(currentH, targetH) : targetH;
|
||||
node._sxapAutoHeight = nextH;
|
||||
node.setSize?.([width, nextH]);
|
||||
if (Math.abs(currentH - nextH) > 1) node.setSize?.([width, nextH]);
|
||||
syncWidgetWidth(node);
|
||||
node.setDirtyCanvas?.(true, true);
|
||||
}
|
||||
@@ -351,6 +334,7 @@ function renderCell(node, entry, imageParams, displayIndex) {
|
||||
cell.title = entryTitle(entry);
|
||||
const cellW = node._sxapCellW || MIN_CELL_W;
|
||||
cell.style.setProperty("--sxap-cell-w", `${cellW}px`);
|
||||
cell.style.setProperty("--sxap-thumb-h", `${node._sxapThumbH || Math.round(cellW * 1.1)}px`);
|
||||
|
||||
cell.ondragover = (event) => {
|
||||
if (!node._sxapDragEntry) return;
|
||||
@@ -379,7 +363,6 @@ function renderCell(node, entry, imageParams, displayIndex) {
|
||||
|
||||
const thumb = document.createElement("img");
|
||||
thumb.className = "sxap-thumb";
|
||||
thumb.style.height = `${Math.max(48, Math.round(cellW * entryAspect(entry)))}px`;
|
||||
if (imageParams) {
|
||||
const url = imageUrl(imageParams);
|
||||
thumb.src = url;
|
||||
|
||||
Reference in New Issue
Block a user