import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { repeat } from "lit/directives/repeat.js"; import { assetUrl } from "../../../core/AssetUrls"; import { Difficulty, GameMapType, MapCategory, mapCategoryOrder, MapInfo, maps, } from "../../../core/game/Game"; import { translateText } from "../../Utils"; import "./MapDisplay"; import { getFavoriteMaps, starIcon, toggleFavoriteMap } from "./MapFavorites"; const randomMap = assetUrl("images/RandomMap.webp"); type MapTab = "featured" | "all" | "favorites"; // Featured grid order: ranked maps first (1 = first), unranked alphabetical. const featuredMaps: MapInfo[] = maps .filter((m) => m.categories.includes("featured")) .sort( (a, b) => (a.featuredRank ?? Number.MAX_SAFE_INTEGER) - (b.featuredRank ?? Number.MAX_SAFE_INTEGER), ); function mapsInCategory(category: MapCategory): MapInfo[] { return maps.filter((m) => m.categories.includes(category)); } @customElement("map-picker") export class MapPicker extends LitElement { @property({ type: String }) selectedMap: GameMapType = GameMapType.World; @property({ type: Boolean }) useRandomMap = false; @property({ type: Boolean }) showMedals = false; @property({ type: Boolean }) randomMapDivider = false; @property({ attribute: false }) mapWins: Map> = new Map(); @property({ attribute: false }) onSelectMap?: (map: GameMapType) => void; @property({ attribute: false }) onSelectRandom?: () => void; @state() private activeTab: MapTab = "featured"; @state() private expandedCategories: Set = new Set(); @state() private favorites: GameMapType[] = getFavoriteMaps(); createRenderRoot() { return this; } private handleToggleFavorite(mapValue: GameMapType) { this.favorites = toggleFavoriteMap(mapValue); } private handleMapSelection(mapValue: GameMapType) { this.onSelectMap?.(mapValue); } private handleSelectRandomMap = () => { this.onSelectRandom?.(); }; private toggleCategory(categoryKey: string) { const expanded = new Set(this.expandedCategories); if (expanded.has(categoryKey)) { expanded.delete(categoryKey); } else { expanded.add(categoryKey); } this.expandedCategories = expanded; } private preventImageDrag(event: DragEvent) { event.preventDefault(); } private getWins(mapValue: GameMapType): Set { return this.mapWins?.get(mapValue) ?? new Set(); } private renderMapCard(map: MapInfo) { return html`
this.handleMapSelection(map.type)} class="cursor-pointer" > this.handleToggleFavorite(map.type)} .translation=${translateText(map.translationKey)} >
`; } private renderMapGrid(mapList: MapInfo[]) { // Keyed by map so cards keep their identity when the list shifts // (e.g. the selected map gets prepended to the featured grid) — // positional reuse would leave stale thumbnails behind. return html`
${repeat( mapList, (map) => map.id, (map) => this.renderMapCard(map), )}
`; } private renderSectionHeading(label: string) { return html`

${label}

`; } private renderCategoryBar(categoryKey: MapCategory, mapList: MapInfo[]) { const expanded = this.expandedCategories.has(categoryKey); return html`
${expanded ? html`
${this.renderMapGrid(mapList)}
` : null}
`; } private renderFeaturedTab() { let featuredMapList = featuredMaps; const selected = maps.find((m) => m.type === this.selectedMap); if ( !this.useRandomMap && selected !== undefined && !featuredMaps.includes(selected) ) { featuredMapList = [selected, ...featuredMaps]; } return html`
${this.renderSectionHeading(translateText("map_categories.featured"))} ${this.renderMapGrid(featuredMapList)}
`; } private renderAllTab() { return html`
${mapCategoryOrder .filter((categoryKey) => categoryKey !== "featured") .map((categoryKey) => this.renderCategoryBar(categoryKey, mapsInCategory(categoryKey)), )}
`; } private renderFavoritesTab() { if (this.favorites.length === 0) { return html`
${starIcon(false, "w-8 h-8")}

${translateText("map_component.favorites_empty")}

`; } const favoriteMaps = this.favorites .map((favorite) => maps.find((m) => m.type === favorite)) .filter((m) => m !== undefined); return html`
${this.renderSectionHeading(translateText("map_categories.favorites"))} ${this.renderMapGrid(favoriteMaps)}
`; } private renderActiveTab() { switch (this.activeTab) { case "all": return this.renderAllTab(); case "favorites": return this.renderFavoritesTab(); default: return this.renderFeaturedTab(); } } private renderTabButton(tab: MapTab, label: string) { const isActive = this.activeTab === tab; return html``; } render() { return html`
${this.renderTabButton("featured", translateText("map.featured"))} ${this.renderTabButton("all", translateText("map.all"))} ${this.renderTabButton("favorites", translateText("map.favorites"))}
${this.renderActiveTab()}
${this.renderSectionHeading(translateText("map_categories.special"))}
`; } }