feat: track selected market creators
This commit is contained in:
@@ -258,6 +258,34 @@ export function syncPluginSortHeaders(
|
||||
});
|
||||
}
|
||||
|
||||
export function syncMarketSelectionState(
|
||||
table: MarketTableDom,
|
||||
selectedAuthorIds: Set<string>
|
||||
): void {
|
||||
table.rows.forEach((rowDom) => {
|
||||
rowDom.selectionCheckbox.dataset.marketSelectionAuthorId = rowDom.authorId;
|
||||
rowDom.selectionCheckbox.checked = selectedAuthorIds.has(rowDom.authorId);
|
||||
});
|
||||
|
||||
if (!table.headerSelectionCheckbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleRows = table.rows.filter((rowDom) =>
|
||||
rowDom.visibilityTargets.some((target) => !target.hidden)
|
||||
);
|
||||
const scopedRows = visibleRows.length > 0 ? visibleRows : table.rows;
|
||||
const selectedCount = scopedRows.filter((rowDom) =>
|
||||
selectedAuthorIds.has(rowDom.authorId)
|
||||
).length;
|
||||
|
||||
table.headerSelectionCheckbox.indeterminate =
|
||||
selectedCount > 0 && selectedCount < scopedRows.length;
|
||||
table.headerSelectionCheckbox.checked =
|
||||
scopedRows.length > 0 && selectedCount === scopedRows.length;
|
||||
table.headerSelectionCheckbox.disabled = scopedRows.length === 0;
|
||||
}
|
||||
|
||||
function syncSyntheticMarketTable(root: ParentNode): MarketTableDom | null {
|
||||
const header = root.querySelector("[data-market-header]") as HTMLElement | null;
|
||||
const body = root.querySelector("[data-market-body]") as HTMLElement | null;
|
||||
@@ -285,9 +313,11 @@ function syncSyntheticMarketTable(root: ParentNode): MarketTableDom | null {
|
||||
const backendMetricsCells = Object.fromEntries(
|
||||
BACKEND_METRIC_COLUMNS.map(({ field }) => [field, ensureSyntheticRowCell(row, field)])
|
||||
) as Record<BackendMetricField, HTMLElement>;
|
||||
const authorId = row.dataset.authorId ?? "";
|
||||
selectionCheckbox.dataset.marketSelectionAuthorId = authorId;
|
||||
|
||||
return {
|
||||
authorId: row.dataset.authorId ?? "",
|
||||
authorId,
|
||||
authorName:
|
||||
row.querySelector('[data-market-field="authorName"]')?.textContent?.trim() ??
|
||||
"",
|
||||
@@ -616,6 +646,7 @@ function syncDivGridRoot(root: HTMLElement): MarketTableDom | null {
|
||||
readDivGridPriceDisplay(priceCells[index]?.textContent),
|
||||
fallbackMarketRow?.price21To60s
|
||||
);
|
||||
selectionCheckbox.dataset.marketSelectionAuthorId = authorId;
|
||||
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
readMarketPageSignature,
|
||||
renderMarketRowState,
|
||||
syncPluginSortHeaders,
|
||||
syncMarketSelectionState,
|
||||
syncMarketTable,
|
||||
type MarketRowDom
|
||||
} from "./dom-sync";
|
||||
@@ -99,6 +100,7 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
let lastKnownPageSignature = "";
|
||||
let needsResync = false;
|
||||
let scheduledSyncTimeoutId: number | null = null;
|
||||
const selectedAuthorIds = new Set<string>();
|
||||
let toolbar: ReturnType<typeof ensurePluginToolbar> | undefined;
|
||||
const observer = mutationObserverFactory(() => {
|
||||
if (isDisposed) {
|
||||
@@ -114,7 +116,14 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
|
||||
const toolbarNeedsRemount =
|
||||
!toolbar || !isPluginToolbarMounted(toolbar.root, options.document);
|
||||
if (nextPageSignature === lastKnownPageSignature && !toolbarNeedsRemount) {
|
||||
const selectionControlsMissing =
|
||||
!options.document.querySelector('[data-market-selection-checkbox="row"]') ||
|
||||
!options.document.querySelector('[data-market-selection-checkbox="header"]');
|
||||
if (
|
||||
nextPageSignature === lastKnownPageSignature &&
|
||||
!toolbarNeedsRemount &&
|
||||
!selectionControlsMissing
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -385,10 +394,78 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
const records = getVisibleOrderedRecords(table);
|
||||
applyRowVisibility(table, new Set(records.map((record) => record.authorId)));
|
||||
applyRowOrder(table, records.map((record) => record.authorId));
|
||||
bindSelectionControls(table);
|
||||
syncMarketSelectionState(table, selectedAuthorIds);
|
||||
lastKnownPageSignature = readMarketPageSignature(options.document);
|
||||
});
|
||||
}
|
||||
|
||||
function bindSelectionControls(table: ReturnType<typeof syncMarketTable>): void {
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
table.rows.forEach((rowDom) => {
|
||||
rowDom.selectionCheckbox.dataset.marketSelectionAuthorId = rowDom.authorId;
|
||||
if (rowDom.selectionCheckbox.dataset.marketSelectionBound === "true") {
|
||||
return;
|
||||
}
|
||||
|
||||
rowDom.selectionCheckbox.dataset.marketSelectionBound = "true";
|
||||
rowDom.selectionCheckbox.addEventListener("change", () => {
|
||||
if (rowDom.selectionCheckbox.checked) {
|
||||
selectedAuthorIds.add(rowDom.authorId);
|
||||
} else {
|
||||
selectedAuthorIds.delete(rowDom.authorId);
|
||||
}
|
||||
|
||||
refreshSelectionControls();
|
||||
});
|
||||
});
|
||||
|
||||
if (!table.headerSelectionCheckbox) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (table.headerSelectionCheckbox.dataset.marketSelectionBound === "true") {
|
||||
return;
|
||||
}
|
||||
|
||||
table.headerSelectionCheckbox.dataset.marketSelectionBound = "true";
|
||||
table.headerSelectionCheckbox.addEventListener("change", () => {
|
||||
const currentTable = syncMarketTable(options.document);
|
||||
if (!currentTable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleRows = currentTable.rows.filter((rowDom) =>
|
||||
rowDom.visibilityTargets.some((target) => !target.hidden)
|
||||
);
|
||||
const scopedRows = visibleRows.length > 0 ? visibleRows : currentTable.rows;
|
||||
if (table.headerSelectionCheckbox?.checked) {
|
||||
scopedRows.forEach((rowDom) => {
|
||||
selectedAuthorIds.add(rowDom.authorId);
|
||||
});
|
||||
} else {
|
||||
scopedRows.forEach((rowDom) => {
|
||||
selectedAuthorIds.delete(rowDom.authorId);
|
||||
});
|
||||
}
|
||||
|
||||
refreshSelectionControls();
|
||||
});
|
||||
}
|
||||
|
||||
function refreshSelectionControls(): void {
|
||||
const table = syncMarketTable(options.document);
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
bindSelectionControls(table);
|
||||
syncMarketSelectionState(table, selectedAuthorIds);
|
||||
}
|
||||
|
||||
function toggleSortFromHeader(field: MarketSortState["field"]): void {
|
||||
activeSort = getNextSortState(activeSort, field);
|
||||
applyCurrentView();
|
||||
|
||||
Reference in New Issue
Block a user