feat: add market dom sync
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import { normalizeRateDisplay } from "../../shared/rate-normalizer";
|
||||
import type { MarketRecord } from "./types";
|
||||
|
||||
export interface MarketRowDom {
|
||||
authorId: string;
|
||||
personalCell: HTMLElement;
|
||||
row: HTMLElement;
|
||||
singleCell: HTMLElement;
|
||||
}
|
||||
|
||||
export interface MarketTableDom {
|
||||
body: HTMLElement;
|
||||
rows: MarketRowDom[];
|
||||
}
|
||||
|
||||
export function syncMarketTable(root: ParentNode): MarketTableDom | null {
|
||||
const header = root.querySelector("[data-market-header]") as HTMLElement | null;
|
||||
const body = root.querySelector("[data-market-body]") as HTMLElement | null;
|
||||
|
||||
if (!header || !body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ensureHeaderCell(header, "singleVideoAfterSearchRate", "单视频看后搜率");
|
||||
ensureHeaderCell(header, "personalVideoAfterSearchRate", "个人视频看后搜率");
|
||||
|
||||
const rows = Array.from(
|
||||
body.querySelectorAll("[data-market-row]")
|
||||
).map((rowElement) => {
|
||||
const row = rowElement as HTMLElement;
|
||||
return {
|
||||
authorId: row.dataset.authorId ?? "",
|
||||
personalCell: ensureRowCell(row, "personalVideoAfterSearchRate"),
|
||||
row,
|
||||
singleCell: ensureRowCell(row, "singleVideoAfterSearchRate")
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
body,
|
||||
rows
|
||||
};
|
||||
}
|
||||
|
||||
export function renderMarketRowState(
|
||||
rowDom: MarketRowDom,
|
||||
record: MarketRecord
|
||||
): void {
|
||||
if (record.status === "success" && record.rates) {
|
||||
rowDom.singleCell.textContent = normalizeRateDisplay(
|
||||
record.rates.singleVideoAfterSearchRate
|
||||
);
|
||||
rowDom.personalCell.textContent = normalizeRateDisplay(
|
||||
record.rates.personalVideoAfterSearchRate
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (record.status === "loading") {
|
||||
rowDom.singleCell.textContent = "加载中...";
|
||||
rowDom.personalCell.textContent = "加载中...";
|
||||
return;
|
||||
}
|
||||
|
||||
if (record.status === "failed") {
|
||||
rowDom.singleCell.textContent = "加载失败";
|
||||
rowDom.personalCell.textContent = "加载失败";
|
||||
return;
|
||||
}
|
||||
|
||||
rowDom.singleCell.textContent = "";
|
||||
rowDom.personalCell.textContent = "";
|
||||
}
|
||||
|
||||
export function applyRowVisibility(
|
||||
table: MarketTableDom,
|
||||
visibleAuthorIds: Set<string>
|
||||
): void {
|
||||
table.rows.forEach((rowDom) => {
|
||||
rowDom.row.hidden = !visibleAuthorIds.has(rowDom.authorId);
|
||||
});
|
||||
}
|
||||
|
||||
export function applyRowOrder(
|
||||
table: MarketTableDom,
|
||||
orderedAuthorIds: string[]
|
||||
): void {
|
||||
const rowById = new Map(table.rows.map((rowDom) => [rowDom.authorId, rowDom]));
|
||||
|
||||
orderedAuthorIds.forEach((authorId) => {
|
||||
const rowDom = rowById.get(authorId);
|
||||
if (rowDom) {
|
||||
table.body.appendChild(rowDom.row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ensureHeaderCell(
|
||||
header: HTMLElement,
|
||||
field: string,
|
||||
label: string
|
||||
): HTMLElement {
|
||||
const existingCell = header.querySelector(
|
||||
`[data-market-header-cell="${field}"]`
|
||||
) as HTMLElement | null;
|
||||
|
||||
if (existingCell) {
|
||||
return existingCell;
|
||||
}
|
||||
|
||||
const nextCell = header.ownerDocument.createElement("div");
|
||||
nextCell.dataset.marketHeaderCell = field;
|
||||
nextCell.textContent = label;
|
||||
header.appendChild(nextCell);
|
||||
return nextCell;
|
||||
}
|
||||
|
||||
function ensureRowCell(row: HTMLElement, field: string): HTMLElement {
|
||||
const existingCell = row.querySelector(
|
||||
`[data-market-row-cell="${field}"]`
|
||||
) as HTMLElement | null;
|
||||
|
||||
if (existingCell) {
|
||||
return existingCell;
|
||||
}
|
||||
|
||||
const nextCell = row.ownerDocument.createElement("span");
|
||||
nextCell.dataset.marketRowCell = field;
|
||||
row.appendChild(nextCell);
|
||||
return nextCell;
|
||||
}
|
||||
Reference in New Issue
Block a user