126 lines
No EOL
4.4 KiB
TypeScript
126 lines
No EOL
4.4 KiB
TypeScript
/**
|
|
* Layout — shared domain-aware formatting for all embed types.
|
|
* Wraps format.ts functions with business logic (Config, Bringer, Leaves).
|
|
*
|
|
* Usage:
|
|
* import { Layout } from "@ui/layout";
|
|
*
|
|
* Layout.wrank(entry, goal, context)
|
|
* Layout.tgCount(done, goal)
|
|
* Layout.kd(k, d)
|
|
* Layout.bringer(char)
|
|
* Layout.cockroach(char, historyKey)
|
|
* Layout.indicators(char, { historyKey })
|
|
* Layout.formatRow(template, tokens)
|
|
*/
|
|
|
|
import { Character, Nation } from "@types";
|
|
import { WRankEntry, WRankWeek } from "@systems/wrank";
|
|
import { Bringer } from "@systems/bringer";
|
|
import { Leaves } from "@systems/leaves";
|
|
import { Emoji } from "@systems/emojis";
|
|
import { format } from "@format";
|
|
import { TGKey } from "@systems/tg-key";
|
|
|
|
// ─── Context ──────────────────────────────────────────────────────────────────
|
|
|
|
export interface NationContext {
|
|
nationHasRank: boolean;
|
|
nationHasDelta: boolean;
|
|
}
|
|
|
|
// ─── Namespace ────────────────────────────────────────────────────────────────
|
|
|
|
export const Layout = {
|
|
/**
|
|
* Format W.Rank prefix — rank + delta with placeholders for alignment.
|
|
*/
|
|
wrank(entry: WRankEntry | null, goal: number, context: NationContext): string {
|
|
return format.wrank.row(entry, goal, context);
|
|
},
|
|
|
|
/**
|
|
* Format TG count as wrank emoji digits.
|
|
* done/goal — goal always gold, done turns gold when >= goal.
|
|
*/
|
|
tgCount(done: number, goal: number): string {
|
|
const doneEmoji = done >= goal
|
|
? (Emoji.get(`wrank_${done}_gold`) || `${done}`)
|
|
: (Emoji.get(`wrank_${done}`) || `${done}`);
|
|
const goalEmoji = Emoji.get(`wrank_${goal}_gold`) || `${goal}`;
|
|
return `${doneEmoji}/${goalEmoji}`;
|
|
},
|
|
|
|
/**
|
|
* Format K/D — returns empty string if both zero.
|
|
*/
|
|
kd(k: number, d: number): string {
|
|
return (k || d) ? format.kd(k, d) : "";
|
|
},
|
|
|
|
/**
|
|
* Bringer indicator — returns " · {bringer emoji}" if char is Bringer.
|
|
*/
|
|
bringer(char: Character, week?: WRankWeek): string {
|
|
const bringer = Bringer.get({ nation: char.nation, week });
|
|
return bringer === char.name ? `${format.bringer(char.nation)}` : "";
|
|
},
|
|
|
|
/**
|
|
* Cockroach indicator — returns " {cockroach}{count}" if char left that TG.
|
|
*/
|
|
cockroach(char: Character, historyKey?: TGKey): string {
|
|
if (!historyKey) return "";
|
|
if (!Leaves.hasLeft({ characterName: char.name, historyKey })) return "";
|
|
return ` ${Leaves.formatIndicator({ characterName: char.name })}`;
|
|
},
|
|
|
|
/**
|
|
* All indicators combined — bringer + cockroach.
|
|
*/
|
|
indicators(char: Character, opts: { historyKey?: TGKey; week?: WRankWeek } = {}): string {
|
|
return Layout.bringer(char, opts.week) + Layout.cockroach(char, opts.historyKey);
|
|
},
|
|
|
|
/**
|
|
* Format a row from a template string with token replacement.
|
|
* Tokens: {wrank} {class} {level} {name} {score} {kd} {tgs} {stats}
|
|
* Unknown tokens are left as-is.
|
|
* Trailing empty tokens and extra spaces are cleaned up.
|
|
*/
|
|
formatRow(template: string, tokens: Record<string, string>): string {
|
|
return template
|
|
.replace(/\{(\w+)\}/g, (_, key) => tokens[key] ?? `{${key}}`)
|
|
.replace(/ +/g, " ") // only collapse regular ASCII spaces, not all \s
|
|
.trim();
|
|
},
|
|
|
|
/**
|
|
* Build nation context from any rows that have a position.
|
|
*/
|
|
nationContext(rows: { position?: { currentRank: number; previousRank?: number } }[]): NationContext {
|
|
return {
|
|
nationHasRank: rows.some((r) => r.position && r.position.currentRank !== 0),
|
|
nationHasDelta: rows.some((r) => r.position?.previousRank !== undefined),
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Build WRankEntry shape from a position object for use with format.wrank.row.
|
|
*/
|
|
wrankEntry(
|
|
char: Character,
|
|
position?: { currentRank: number; previousRank?: number },
|
|
weeklyPoints = 0,
|
|
tgCount = 0
|
|
): WRankEntry | null {
|
|
if (!position || position.currentRank === 0) return null;
|
|
return {
|
|
character: char,
|
|
weeklyPoints,
|
|
tgCount,
|
|
currentRank: position.currentRank,
|
|
previousRank: position.previousRank,
|
|
};
|
|
},
|
|
}; |