import { useState } from "react"; import { MousePointerClick } from "lucide-react"; export type SegmentType = | "ic" | "dc" | "modifier" | "conjunction" | "punct" | "subject" | "verb"; export interface Segment { text: string; type: SegmentType; label?: string; } export interface ClauseExample { title: string; segments: Segment[]; } interface ClauseBreakdownWidgetProps { examples: ClauseExample[]; accentColor?: string; } const TYPE_STYLES: Record< SegmentType, { bg: string; text: string; border: string; ring: string } > = { ic: { bg: "bg-blue-100", text: "text-blue-800", border: "border-blue-300", ring: "#93c5fd", }, dc: { bg: "bg-green-100", text: "text-green-800", border: "border-green-300", ring: "#86efac", }, modifier: { bg: "bg-orange-100", text: "text-orange-800", border: "border-orange-300", ring: "#fdba74", }, conjunction: { bg: "bg-purple-100", text: "text-purple-800", border: "border-purple-300", ring: "#c4b5fd", }, subject: { bg: "bg-sky-100", text: "text-sky-800", border: "border-sky-300", ring: "#7dd3fc", }, verb: { bg: "bg-rose-100", text: "text-rose-800", border: "border-rose-300", ring: "#fda4af", }, punct: { bg: "bg-gray-100", text: "text-gray-600", border: "border-gray-300", ring: "#d1d5db", }, }; const TYPE_LABELS: Record = { ic: "Independent Clause", dc: "Dependent Clause", modifier: "Modifier", conjunction: "Conjunction", subject: "Subject", verb: "Verb / Predicate", punct: "Punctuation", }; // Pre-resolved tab accent classes (avoids Tailwind purge issues with dynamic strings) const TAB_ACTIVE: Record = { purple: "border-b-2 border-purple-600 text-purple-700 bg-white", teal: "border-b-2 border-teal-600 text-teal-700 bg-white", fuchsia: "border-b-2 border-fuchsia-600 text-fuchsia-700 bg-white", amber: "border-b-2 border-amber-600 text-amber-700 bg-white", }; export default function ClauseBreakdownWidget({ examples, accentColor = "purple", }: ClauseBreakdownWidgetProps) { const [activeTab, setActiveTab] = useState(0); const [selected, setSelected] = useState(null); const example = examples[activeTab]; const switchTab = (i: number) => { setActiveTab(i); setSelected(null); }; const selectedSeg = selected !== null ? example.segments[selected] : null; const tabActive = TAB_ACTIVE[accentColor] ?? TAB_ACTIVE.purple; // Unique labeled segment types for the legend const legendTypes = Array.from( new Set(example.segments.filter((s) => s.label).map((s) => s.type)), ); return (
{/* Tab strip */} {examples.length > 1 && (
{examples.map((ex, i) => ( ))}
)} {examples.length === 1 && (

{example.title}

)} {/* Instruction */}

Click any colored part to see its grammatical role

{/* Sentence display */}
{example.segments.map((seg, i) => { if (!seg.label) { // Punctuation / unlabeled — plain unstyled text, not clickable return ( {seg.text} ); } const style = TYPE_STYLES[seg.type]; const isSelected = selected === i; return ( setSelected(isSelected ? null : i)} className={`inline cursor-pointer rounded px-1 py-0.5 mx-0.5 transition-all ${style.bg} ${style.text} ${ isSelected ? `border-2 ${style.border} font-semibold` : `border ${style.border} hover:opacity-80` }`} style={ isSelected ? { outline: `2.5px solid ${style.ring}`, outlineOffset: "1px", } : {} } > {seg.text} ); })}
{/* Selection indicator */} {selectedSeg ? (

{selectedSeg.label ?? TYPE_LABELS[selectedSeg.type]}

"{selectedSeg.text.trim()}"

) : (

No element selected — click a colored span above.

)}
{/* Legend */}
{legendTypes.map((type) => { const style = TYPE_STYLES[type]; return ( {TYPE_LABELS[type]} ); })}
); }