fix: align market selection checkboxes with rows

- Fix checkbox column misalignment with creator list items
- Add syncContentCellHeight to keep selection cells aligned with native rows
- Add MarketAlignmentDiagnostic for debugging alignment issues
- Update documentation to clarify dist-release folder usage
- Add npm run build:release step to colleague setup guide
This commit is contained in:
2026-05-08 00:39:15 +08:00
parent ecfe136628
commit 02be56888a
7 changed files with 276 additions and 3 deletions
+52 -2
View File
@@ -99,10 +99,39 @@ export interface MarketTableDom {
rows: MarketRowDom[];
}
export interface MarketAlignmentDiagnostic {
authorId: string;
authorName: string;
checkboxTop: number;
rowTop: number;
topDelta: number;
}
export function syncMarketTable(root: ParentNode): MarketTableDom | null {
return syncSyntheticMarketTable(root) ?? syncDivGridMarketTable(root);
}
export function readMarketAlignmentDiagnostics(
table: MarketTableDom
): MarketAlignmentDiagnostic[] {
return table.rows.map((rowDom) => {
const selectionCell = rowDom.selectionCheckbox.parentElement;
const selectionRect = selectionCell?.getBoundingClientRect();
const rowRect = rowDom.row.getBoundingClientRect();
const checkboxTop = selectionRect?.top ?? Number.NaN;
const rowTop = rowRect.top ?? Number.NaN;
return {
authorId: rowDom.authorId,
authorName: rowDom.authorName,
checkboxTop,
rowTop,
topDelta: checkboxTop - rowTop
};
});
}
export function readMarketPageSignature(root: ParentNode): string {
const document = getOwnerDocument(root);
const explicitPageIndex =
@@ -801,14 +830,15 @@ function syncDivColumnCells(
const templateCells = getDirectContentCells(templateColumn);
for (let index = 0; index < rowCount; index += 1) {
const existingCell = getDirectContentCells(column)[index] ?? null;
const templateCell =
templateCells[index] ?? templateCells[templateCells.length - 1] ?? null;
if (existingCell) {
existingCell.dataset.marketRowCell = field;
applyPluginContentCellStyles(existingCell);
syncContentCellHeight(existingCell, templateCell);
continue;
}
const templateCell =
templateCells[index] ?? templateCells[templateCells.length - 1] ?? null;
const nextCell =
field === SELECTION_COLUMN_KEY
? templateCell
@@ -820,11 +850,31 @@ function syncDivColumnCells(
nextCell.dataset.marketRowCell = field;
applyColumnWidth(nextCell, field);
applyPluginContentCellStyles(nextCell);
syncContentCellHeight(nextCell, templateCell);
nextCell.textContent = "";
column.appendChild(nextCell);
}
}
function syncContentCellHeight(
cell: HTMLElement,
templateCell: HTMLElement | null
): void {
if (!templateCell) {
return;
}
const measuredHeight = Math.round(templateCell.getBoundingClientRect().height);
const nextHeight =
measuredHeight > 0 ? `${measuredHeight}px` : templateCell.style.height;
if (nextHeight) {
cell.style.height = nextHeight;
} else {
cell.style.removeProperty("height");
}
}
function applyPluginHeaderCellStyles(cell: HTMLElement): void {
cell.style.display = "flex";
cell.style.alignItems = "center";
+56
View File
@@ -4,6 +4,7 @@ import { createBatchPayload, type BatchPayload } from "./batch-payload";
import {
applyRowOrder,
applyRowVisibility,
readMarketAlignmentDiagnostics,
readMarketPageSignature,
renderMarketRowState,
syncPluginSortHeaders,
@@ -396,6 +397,7 @@ export function createMarketController(options: CreateMarketControllerOptions) {
applyRowOrder(table, records.map((record) => record.authorId));
bindSelectionControls(table);
syncMarketSelectionState(table, selectedAuthorIds);
logMarketAlignmentDiagnosticsIfEnabled(options.window, table, "applyCurrentView");
lastKnownPageSignature = readMarketPageSignature(options.document);
});
}
@@ -662,12 +664,23 @@ export function createMarketController(options: CreateMarketControllerOptions) {
}
const step = Math.max(scrollContainer.clientHeight, 240);
logMarketScrollTraceIfEnabled(options.window, {
event: "start",
maxScrollTop,
originalScrollTop,
step
});
for (
let nextScrollTop = Math.min(originalScrollTop + step, maxScrollTop);
nextScrollTop > originalScrollTop && nextScrollTop <= maxScrollTop;
nextScrollTop = Math.min(nextScrollTop + step, maxScrollTop)
) {
setScrollTop(scrollContainer, nextScrollTop);
logMarketScrollTraceIfEnabled(options.window, {
event: "step",
nextScrollTop,
scrollTop: scrollContainer.scrollTop
});
hydrationSnapshot = await collectCurrentPageSnapshotsUntilSettled();
if (
hydrationSnapshot.missingDefaultFieldCount === 0 &&
@@ -683,6 +696,10 @@ export function createMarketController(options: CreateMarketControllerOptions) {
if (scrollContainer.scrollTop !== originalScrollTop) {
setScrollTop(scrollContainer, originalScrollTop);
logMarketScrollTraceIfEnabled(options.window, {
event: "restore",
restoredScrollTop: scrollContainer.scrollTop
});
}
}
@@ -1009,6 +1026,45 @@ function setScrollTop(element: HTMLElement, top: number): void {
element.dispatchEvent(new Event("scroll"));
}
function logMarketAlignmentDiagnosticsIfEnabled(
window: Window,
table: ReturnType<typeof syncMarketTable>,
label: string
): void {
if (!isMarketDebugLoggingEnabled(window) || !table) {
return;
}
const diagnostics = readMarketAlignmentDiagnostics(table);
console.info(`[SCES debug] ${label} alignment`, diagnostics);
}
function logMarketScrollTraceIfEnabled(
window: Window,
payload: Record<string, unknown>
): void {
if (!isMarketDebugLoggingEnabled(window)) {
return;
}
console.info("[SCES debug] submit/export scroll", payload);
}
function isMarketDebugLoggingEnabled(window: Window): boolean {
try {
const searchParams = new URL(window.location.href).searchParams;
if (searchParams.get("scesDebug") === "1") {
return true;
}
} catch {}
try {
return window.localStorage.getItem("scesDebugMarket") === "1";
} catch {
return false;
}
}
function readCurrentPageRows(document: Document): MarketRowSnapshot[] {
const table = syncMarketTable(document);
if (!table) {