Introduce a settings modal (opened from the kebab menu) with a rules edition selector for condition tooltip descriptions and a theme picker replacing the inline cycle button. About half the conditions have meaningful mechanical differences between editions. - Add description5e field to ConditionDefinition with 5e (2014) text - Add RulesEditionProvider context with localStorage persistence - Create SettingsModal with Conditions and Theme sections - Wire condition tooltips to edition-aware descriptions - Fix 6 inaccurate 5.5e condition descriptions - Update spec 003 with stories CC-3, CC-8 and FR-095–FR-102 Closes #12 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
1.2 KiB
TypeScript
56 lines
1.2 KiB
TypeScript
import { type ReactNode, useRef, useState } from "react";
|
|
import { createPortal } from "react-dom";
|
|
|
|
interface TooltipProps {
|
|
content: string;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function Tooltip({
|
|
content,
|
|
children,
|
|
className,
|
|
}: Readonly<TooltipProps>) {
|
|
const ref = useRef<HTMLSpanElement>(null);
|
|
const [pos, setPos] = useState<{ top: number; left: number } | null>(null);
|
|
|
|
function show() {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
const rect = el.getBoundingClientRect();
|
|
setPos({
|
|
top: rect.top - 4,
|
|
left: rect.left + rect.width / 2,
|
|
});
|
|
}
|
|
|
|
function hide() {
|
|
setPos(null);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<span
|
|
ref={ref}
|
|
onPointerEnter={show}
|
|
onPointerLeave={hide}
|
|
className={className ?? "inline-flex"}
|
|
>
|
|
{children}
|
|
</span>
|
|
{pos !== null &&
|
|
createPortal(
|
|
<div
|
|
role="tooltip"
|
|
className="pointer-events-none fixed z-[60] max-w-64 -translate-x-1/2 -translate-y-full whitespace-pre-line rounded-md border border-border bg-background px-2.5 py-1.5 text-foreground text-xs leading-snug shadow-lg"
|
|
style={{ top: pos.top, left: pos.left }}
|
|
>
|
|
{content}
|
|
</div>,
|
|
document.body,
|
|
)}
|
|
</>
|
|
);
|
|
}
|