Update Healer Man server
This commit is contained in:
1 parent
a868401ebc
commit
48a678dcc5
295 files changed
+7644
-467
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import { lazy, Suspense, useEffect, useRef, useState, type CSSProperties } from "react";
|
||||
import { BOSS_CARD_RARITIES, BOSS_CARD_RARITY_INFO } from "../game/bossCards";
|
||||
import type { DungeonBossCard as BossCard } from "../game/dungeonBossCards";
|
||||
import "./dungeonBossCards.css";
|
||||
|
||||
const BossCardPortrait = lazy(() => import("./BossCardPortrait"));
|
||||
|
||||
function DungeonBossModel({ card }: { readonly card: BossCard }) {
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const element = container.current;
|
||||
if (!element || !card.model) return;
|
||||
if (typeof IntersectionObserver === "undefined") {
|
||||
setVisible(true);
|
||||
return;
|
||||
}
|
||||
const observer = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting), {
|
||||
rootMargin: "40px",
|
||||
threshold: 0.01,
|
||||
});
|
||||
observer.observe(element);
|
||||
return () => observer.disconnect();
|
||||
}, [card.model]);
|
||||
|
||||
return <div ref={container} className="dungeon-boss-card__portrait">
|
||||
{!card.model ? <span className="dungeon-boss-card__model-status">Model unavailable</span>
|
||||
: visible ? <Suspense fallback={<span className="dungeon-boss-card__model-status">Loading model…</span>}>
|
||||
<BossCardPortrait model={card.model} name={card.name} installed />
|
||||
</Suspense> : <span className="dungeon-boss-card__model-status">Loading model…</span>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
export function DungeonBossCards({ cards, compact = false }: {
|
||||
readonly cards: readonly BossCard[];
|
||||
readonly compact?: boolean;
|
||||
}) {
|
||||
if (!cards.length) return <p className="dungeon-boss-cards__empty">No boss cards are listed for this dungeon.</p>;
|
||||
return <div className={`dungeon-boss-cards ${compact ? "dungeon-boss-cards--compact" : ""}`}>
|
||||
{cards.map((card, index) => {
|
||||
const highestRarity = card.ownedRarities.at(-1);
|
||||
const accent = highestRarity ? BOSS_CARD_RARITY_INFO[highestRarity].color : "#9a947f";
|
||||
return <article key={card.key} className={`dungeon-boss-card ${card.copies ? "is-owned" : ""}`}
|
||||
style={{ "--boss-card-accent": accent } as CSSProperties}
|
||||
aria-label={`${card.name}, ${card.copies ? `${card.copies} ${card.copies === 1 ? "card" : "cards"} owned` : "not collected"}`}>
|
||||
<header><span>Boss {String(index + 1).padStart(2, "0")}</span><strong>{card.copies ? "Collected" : "Uncollected"}</strong></header>
|
||||
<DungeonBossModel card={card} />
|
||||
<div className="dungeon-boss-card__body">
|
||||
<h3>{card.name}</h3>
|
||||
<p>{card.copies ? `${card.copies} ${card.copies === 1 ? "copy" : "copies"} owned` : "Card not collected"}</p>
|
||||
</div>
|
||||
<footer aria-label="Card rarities">
|
||||
{BOSS_CARD_RARITIES.map((rarity) => <span key={rarity}
|
||||
className={card.ownedRarities.includes(rarity) ? "is-owned" : ""}
|
||||
style={{ "--rarity-color": BOSS_CARD_RARITY_INFO[rarity].color } as CSSProperties}
|
||||
title={`${BOSS_CARD_RARITY_INFO[rarity].label}: ${card.ownedRarities.includes(rarity) ? "owned" : "not owned"}`}>
|
||||
{rarity.charAt(0).toUpperCase()}
|
||||
</span>)}
|
||||
</footer>
|
||||
</article>;
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
Reference in new issue
Block a user