Files
community-rule/lib/create/methodCardDisplayOrder.ts
T
2026-04-29 18:29:16 -06:00

68 lines
2.0 KiB
TypeScript

/**
* Reorders facet-ranked method presets so explicitly confirmed selections pin
* to the top while the remainder keeps score-based ranking (recommended before
* default).
*/
/** Selected ids first (selection array order); then tail in `ranked` order. */
export function orderRankedMethodsWithPinnedSelection<T extends { id: string }>(
rankedMethods: readonly T[],
selectedIds: readonly string[],
pinActive: boolean,
): T[] {
if (!pinActive || selectedIds.length === 0) {
return [...rankedMethods];
}
const byId = new Map(rankedMethods.map((m) => [m.id, m] as const));
const head: T[] = [];
const picked = new Set<string>();
for (const id of selectedIds) {
const row = byId.get(id);
if (!row || picked.has(id)) continue;
picked.add(id);
head.push(row);
}
const tail = rankedMethods.filter((m) => !picked.has(m.id));
return [...head, ...tail];
}
/**
* Prefer selected ids in compact slots (up to `limit`), then facet-derived
* `baseCompact.compactCardIds`, then remaining methods in showcase order so
* selected cards surface even when they are outside the unpinned facet top-N.
*/
export function mergeCompactCardIdsWithPinnedSelected(
showcaseOrderIds: readonly string[],
baseCompactCardIds: readonly string[],
selectedIds: readonly string[],
pinActive: boolean,
limit: number,
): string[] {
if (!pinActive || selectedIds.length === 0) {
return [...baseCompactCardIds].slice(0, limit);
}
const valid = new Set(showcaseOrderIds);
const out: string[] = [];
const seen = new Set<string>();
for (const id of selectedIds) {
if (out.length >= limit) break;
if (!valid.has(id) || seen.has(id)) continue;
seen.add(id);
out.push(id);
}
for (const id of baseCompactCardIds) {
if (out.length >= limit) break;
if (!valid.has(id) || seen.has(id)) continue;
seen.add(id);
out.push(id);
}
for (const id of showcaseOrderIds) {
if (out.length >= limit) break;
if (seen.has(id)) continue;
seen.add(id);
out.push(id);
}
return out;
}