import type { ActivityCost, SpellReference } from "@initiative/domain";
import { DetailPopover } from "./detail-popover.js";
import { RichDescription } from "./rich-description.js";
import { ActivityIcon } from "./stat-block-parts.js";
interface SpellDetailPopoverProps {
readonly spell: SpellReference;
readonly anchorRect: DOMRect;
readonly onClose: () => void;
}
const RANK_LABELS = [
"Cantrip",
"1st",
"2nd",
"3rd",
"4th",
"5th",
"6th",
"7th",
"8th",
"9th",
"10th",
];
function formatRank(rank: number | undefined): string {
if (rank === undefined) return "";
return RANK_LABELS[rank] ?? `Rank ${rank}`;
}
function parseActionCost(cost: string): ActivityCost | null {
if (cost === "free") return { number: 1, unit: "free" };
if (cost === "reaction") return { number: 1, unit: "reaction" };
const n = Number(cost);
if (n >= 1 && n <= 3) return { number: n, unit: "action" };
return null;
}
function SpellActionCost({ cost }: Readonly<{ cost: string | undefined }>) {
if (!cost) return null;
const activity = parseActionCost(cost);
if (activity) {
return (
);
}
return {cost};
}
function SpellHeader({ spell }: Readonly<{ spell: SpellReference }>) {
return (
{spell.name}
);
}
function SpellTraits({ traits }: Readonly<{ traits: readonly string[] }>) {
if (traits.length === 0) return null;
return (
{traits.map((t) => (
{t}
))}
);
}
function LabeledValue({
label,
value,
}: Readonly<{ label: string; value: string }>) {
return (
<>
{label} {value}
>
);
}
function SpellRangeLine({ spell }: Readonly<{ spell: SpellReference }>) {
const items: { label: string; value: string }[] = [];
if (spell.range) items.push({ label: "Range", value: spell.range });
if (spell.target) items.push({ label: "Target", value: spell.target });
if (spell.area) items.push({ label: "Area", value: spell.area });
if (items.length === 0) return null;
return (
{items.map((item, i) => (
{i > 0 ? "; " : ""}
))}
);
}
function SpellMeta({ spell }: Readonly<{ spell: SpellReference }>) {
const hasTraditions =
spell.traditions !== undefined && spell.traditions.length > 0;
return (
{spell.rank === undefined ? null : (
{formatRank(spell.rank)}
{hasTraditions ? (
{" "}
({spell.traditions?.join(", ")})
) : null}
)}
{spell.duration ? (
) : null}
{spell.defense ? (
) : null}
);
}
function SpellDetailContent({ spell }: Readonly<{ spell: SpellReference }>) {
return (
{spell.description ? (
) : (
No description available.
)}
{spell.heightening ? (
) : null}
);
}
export function SpellDetailPopover({
spell,
anchorRect,
onClose,
}: Readonly) {
return (
);
}