Expandable Card Plugin Component (components/Editor/expandable-card.tsx)¶
Overview¶
A React component that displays cited evidence in expandable cards with text highlighting capabilities. The component manages text highlighting, card expansion states, and provides smooth transitions for interactive elements.
Features¶
- Text highlighting
- Card expansion
- Evidence display
- Color transitions
- UUID management
- Content preview
- Overflow handling
- Dynamic sizing
- State management
- Smooth animations
Implementation¶
type CitedEvidenceProps = {
citedEvidence?: citedEvidence | null;
isHighlighted?: boolean | null;
uuids?: string[] | null;
selectedUUIDIndex?: number | 0 | null;
};
export function ExpandableCardPlugin({
citedEvidence,
isHighlighted,
uuids,
selectedUUIDIndex
}: CitedEvidenceProps) {
const [expandedCardIndex, setExpandedCardIndex] = useState<number | null>(null);
const [isEditing, setIsEditing] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const highlightedColour = '#EE2A7B';
const transparentColour = 'white';
const [selectedUUID, setSelectedUUID] = useState<string | null>(null);
const getHighlightColor = useCallback((uuid: string) => {
return uuid === selectedUUID ? highlightedColour : transparentColour;
}, [selectedUUID, highlightedColour, transparentColour]);
const highlightText = (beforeText: string, afterText: string | null, highlightedText: string, uuid: string) => {
const color = getHighlightColor(uuid);
return (
<div>
<span>{beforeText}</span>
<span
className={uuid}
style={{
backgroundColor: color,
padding: '0.25rem',
borderRadius: '0.25rem',
transition: 'all 0.5s ease-in-out'
}}
>
{highlightedText}
</span>
{afterText && <span>{afterText}</span>}
</div>
);
};
const highlightSpecifiedText = (citedEvidence: citedEvidence) => {
const allHighlightedNodes: JSX.Element[] = [];
citedEvidence.forEach(evidence => {
const textContent = evidence.evidence_text;
const textContentArray = textContent.split(" ");
const highlightedTextRanges: HighlightedTextNodes = [];
evidence.citations.forEach((citation, index) => {
const { uuid, evidence_start_index, evidence_end_index } = citation;
highlightedTextRanges.push({
index,
uuid,
startIndex: evidence_start_index,
endIndex: evidence_end_index
});
});
const highlightedTextRangesSorted = sortBasedOnStartIndex(highlightedTextRanges);
const newNodes: JSX.Element[] = [];
// Process text ranges and create highlighted nodes
let lowerBound = 0;
highlightedTextRangesSorted.forEach((node, i) => {
if (lowerBound <= node.startIndex) {
const beforeText = textContentArray.slice(lowerBound, node.startIndex - 1).join(" ");
const highlightedText = textContentArray.slice(node.startIndex - 1, node.endIndex + 1).join(" ");
const afterText = i === highlightedTextRangesSorted.length - 1
? textContentArray.slice(node.endIndex + 1).join(" ")
: null;
newNodes.push(highlightText(beforeText, afterText, highlightedText, node.uuid));
lowerBound = node.endIndex + 1;
}
});
allHighlightedNodes.push(<div key={evidence.uuid}>{newNodes}</div>);
});
return allHighlightedNodes;
};
return (
<div className="ml-4 flex overflow-x-auto space-x-4">
{(isHighlighted ? highlightSpecifiedText(citedEvidence) : citedEvidence.map(evidence => (
<div key={evidence.uuid}>{evidence.evidence_text}</div>
))).map((highlightedContent, nodeIndex) => (
<Card
key={nodeIndex}
className={`max-w-[500px] flex-shrink-0 mb-4 transition-all duration-300 ease-in-out ${
isExpanded ? 'max-h-none' : 'max-h-[150px]'
}`}
>
<CardContent className={`p-4 flex flex-col h-full ${!isExpanded && 'overflow-hidden'}`}>
<div className="text-sm text-black flex-grow">
{highlightedContent}
</div>
</CardContent>
</Card>
))}
</div>
);
}
Dependencies¶
import { JSX, useState, useEffect, useCallback } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { citedEvidence } from "./citedEvidence"
Components Used¶
Notes¶
- Manages text highlighting
- Handles card expansion
- Processes evidence data
- Updates color states
- Maintains UUID tracking
- Implements transitions
- Sorts text ranges
- Preserves text content
- Optimizes performance
- Ensures accessibility