import { assetUrl } from "@openfront/shared/AssetUrls"; import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; interface IconFile { file: string; /** Drawn as an to keep its colours; other icons take currentColor. */ multicolor?: boolean; /** A 4:5 file whose art is its top square. */ crop?: boolean; } export const OF_ICONS = { close: { file: "XIcon.svg", crop: true }, back: { file: "BackIconWhite.svg" }, check: { file: "CheckIconWhite.svg" }, "chevron-right": { file: "ChevronRightIconWhite.svg" }, plus: { file: "PlusIconWhite.svg" }, hourglass: { file: "HourglassIconWhite.svg" }, settings: { file: "SettingIconWhite.svg" }, graphics: { file: "MonitorIconWhite.svg" }, terrain: { file: "TreeIconWhite.svg" }, debug: { file: "BugIconWhite.svg" }, save: { file: "SaveIconWhite.svg" }, exit: { file: "ExitIconWhite.svg" }, share: { file: "ShareIconWhite.svg" }, land: { file: "ClaimIcon.svg" }, leaderboard: { file: "LeaderboardIconSolidWhite.svg" }, team: { file: "TeamIconSolidWhite.svg" }, gold: { file: "GoldCoinIcon.svg", multicolor: true }, troops: { file: "SoldierIcon.svg" }, traitor: { file: "TraitorIcon.svg" }, trade: { file: "TradingIconWhite.svg" }, embargo: { file: "StopIconWhite.svg" }, alliance: { file: "AllianceIconWhite.svg" }, "break-alliance": { file: "TraitorIconWhite.svg" }, chat: { file: "ChatIconWhite.svg" }, emoji: { file: "EmojiIconWhite.svg" }, target: { file: "TargetIconWhite.svg" }, "donate-troops": { file: "DonateTroopIconWhite.svg" }, "donate-gold": { file: "DonateGoldIconWhite.svg" }, report: { file: "SirenIconWhite.svg" }, shield: { file: "ShieldIconWhite.svg" }, nation: { file: "CityIconWhite.svg" }, tribe: { file: "SwordIconWhite.svg" }, swap: { file: "SwapIconWhite.svg" }, sword: { file: "SwordIconWhite.svg" }, max: { file: "UpperLimitIcon.svg" }, info: { file: "InfoIconRegularWhite.svg" }, warning: { file: "WarningIconWhite.svg" }, lock: { file: "LockIconWhite.svg" }, city: { file: "CityIconWhite.svg" }, factory: { file: "FactoryIconWhite.svg" }, port: { file: "PortIcon.svg" }, "missile-silo": { file: "MissileSiloIconWhite.svg" }, "sam-launcher": { file: "SamLauncherIconWhite.svg" }, warship: { file: "BattleshipIconWhite.svg" }, "atom-bomb": { file: "NukeIconWhite.svg" }, "hydrogen-bomb": { file: "MushroomCloudIconWhite.svg", crop: true }, mirv: { file: "MIRVIcon.svg" }, } as const satisfies Record; export type OfIconName = keyof typeof OF_ICONS; /** * One of the game's icons, painted in the current text colour. Decorative * only: the control around it carries the accessible name. */ @customElement("of-icon") export class OfIcon extends LitElement { @property() name!: OfIconName; @property({ type: Number }) size: number = 16; createRenderRoot() { return this; } render() { const icon: IconFile | undefined = OF_ICONS[this.name]; if (icon === undefined) { return html``; } const src = assetUrl(`images/${icon.file}`); if (icon.multicolor) { return html``; } // The mask uses the file's alpha, so its fill colour does not matter. A // cropped file is sized to the box's width, so what is under its top // square falls below the box. const mask = `url('${src}') ${icon.crop ? "top / 100% 125%" : "center / contain"} no-repeat`; return html``; } }