A D&D stat block opened for a combatant now offers a Min/Avg/Max toggle below the Hit Points line, deriving both ends from the Hit Dice formula (3d6 + 6 -> 9 / 16 / 24). The switch is applied as a delta, so manual HP edits and damage already taken survive it. Unlike the PF2e weak/elite adjustment this is a convenience tool, not a rules mechanic: nothing but HP changes and the combatant is not renamed. The toggle is hidden when the HP field carries prose instead of a dice pool. Also extracts a SegmentedControl primitive. Game system, theme and the PF2e weak/elite toggle were three hand-rolled copies of the same markup, which is why the D&D toggle first came out chunkier and in the wrong accent color. All four now share one definition, and segments expose aria-pressed instead of signalling the active state by color alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
90 lines
2.0 KiB
TypeScript
90 lines
2.0 KiB
TypeScript
import type { RulesEdition } from "@initiative/domain";
|
|
import { Monitor, Moon, Sun } from "lucide-react";
|
|
import { useRulesEditionContext } from "../contexts/rules-edition-context.js";
|
|
import { useThemeContext } from "../contexts/theme-context.js";
|
|
import { Dialog, DialogHeader } from "./ui/dialog.js";
|
|
import type { SegmentedOption } from "./ui/segmented-control.js";
|
|
import { SegmentedControl } from "./ui/segmented-control.js";
|
|
|
|
interface SettingsModalProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const EDITION_OPTIONS: SegmentedOption<RulesEdition>[] = [
|
|
{ value: "5e", label: "5e (2014)" },
|
|
{ value: "5.5e", label: "5.5e (2024)" },
|
|
{ value: "pf2e", label: "Pathfinder 2e" },
|
|
];
|
|
|
|
type ThemePreference = "system" | "light" | "dark";
|
|
|
|
const THEME_OPTIONS: SegmentedOption<ThemePreference>[] = [
|
|
{
|
|
value: "system",
|
|
label: (
|
|
<>
|
|
<Monitor size={14} />
|
|
System
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
value: "light",
|
|
label: (
|
|
<>
|
|
<Sun size={14} />
|
|
Light
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
value: "dark",
|
|
label: (
|
|
<>
|
|
<Moon size={14} />
|
|
Dark
|
|
</>
|
|
),
|
|
},
|
|
];
|
|
|
|
export function SettingsModal({ open, onClose }: Readonly<SettingsModalProps>) {
|
|
const { edition, setEdition } = useRulesEditionContext();
|
|
const { preference, setPreference } = useThemeContext();
|
|
|
|
return (
|
|
<Dialog open={open} onClose={onClose} className="card-glow w-full max-w-sm">
|
|
<DialogHeader title="Settings" onClose={onClose} />
|
|
|
|
<div className="flex flex-col gap-5">
|
|
<div>
|
|
<span className="mb-2 block font-medium text-muted-foreground text-sm">
|
|
Game System
|
|
</span>
|
|
<SegmentedControl
|
|
options={EDITION_OPTIONS}
|
|
value={edition}
|
|
onChange={setEdition}
|
|
stretch
|
|
label="Game System"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<span className="mb-2 block font-medium text-muted-foreground text-sm">
|
|
Theme
|
|
</span>
|
|
<SegmentedControl
|
|
options={THEME_OPTIONS}
|
|
value={preference}
|
|
onChange={setPreference}
|
|
stretch
|
|
label="Theme"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Dialog>
|
|
);
|
|
}
|