Skip to content

Character Highlight Component (components/Editor/character-highlight.tsx)

Overview

A React component that provides controls for revealing and navigating through highlighted evidence in text. The component features animated buttons for toggling evidence visibility and cycling through different highlighted sections using UUIDs.

Features

  • Evidence visibility toggle
  • UUID-based navigation
  • Circular navigation
  • Animated transitions
  • Progress tracking
  • State management
  • Keyboard accessibility
  • Responsive design
  • Error handling
  • Event callbacks
  • Initial state handling
  • Expandable controls
  • Visual feedback
  • Aria labels
  • Cleanup handling

Implementation

interface CharacterHighlightProps {
  initialHighlightState?: boolean;
  isHighlighted?: boolean;
  onHighlightChange?: (value: boolean) => void;
  uuids?: string[];
  currentIndex: number;
  onIndexChange?: (value: number) => void;
}

export function CharacterHighlight({
  isHighlighted,
  onHighlightChange,
  uuids = [],
  currentIndex: initialIndex,
  onIndexChange
}: CharacterHighlightProps) {
  const initialIndexValue = uuids.length > 0 ? 0 : initialIndex;
  const [currentIndex, setCurrentIndex] = useState(initialIndexValue);
  const [isExpanded, setIsExpanded] = useState(false);
  const [selectedUuid, setSelectedUuid] = useState(uuids[initialIndexValue] || null);

  useEffect(() => {
    if (uuids.length > 0) {
      setCurrentIndex(0);
      handleUuidChange(0);
    }
  }, [uuids]);

  const handleHighlight = useCallback(() => {
    const newHighlightState = !isHighlighted;
    if (onHighlightChange) {
      onHighlightChange(newHighlightState);
    }
    if (newHighlightState && uuids.length > 0) {
      setIsExpanded(true);
      setCurrentIndex(0);
      handleUuidChange(0);
    } else {
      setIsExpanded(false);
    }
  }, [isHighlighted, onHighlightChange, uuids]);

  const handleForward = useCallback(() => {
    const newIndex = (currentIndex + 1) % uuids.length;
    setCurrentIndex(newIndex);
    handleUuidChange(newIndex);
  }, [currentIndex, uuids.length]);

  const handleBackward = useCallback(() => {
    const newIndex = (currentIndex - 1 + uuids.length) % uuids.length;
    setCurrentIndex(newIndex);
    handleUuidChange(newIndex);
  }, [currentIndex, uuids.length]);

  const handleUuidChange = (index: number) => {
    if (uuids[index]) {
      setSelectedUuid(uuids[index]);
      if (onIndexChange) {
        onIndexChange(index);
      }  
    }
  };

  return (
    <div className="flex items-center">
      <Button onClick={handleHighlight} className="rounded-full shadow-lg py-1.5" variant="outline">
        {isHighlighted ? 'Hide' : 'Reveal'} evidence
      </Button>
      {isHighlighted && isExpanded && (
        <AnimatePresence>
          <motion.div
            initial={{ width: 0, opacity: 0 }}
            animate={{ width: 'auto', opacity: 1 }}
            exit={{ width: 0, opacity: 0 }}
            transition={{ duration: 0.3 }}
            className="ml-2 flex items-center overflow-hidden"
          >
            <Button onClick={handleBackward} variant="ghost" size="icon" className="mr-1" aria-label="Previous UUID">
              <ChevronLeft className="h-4 w-4" />
            </Button>
            <div className="text-sm text-muted-foreground">
              {currentIndex + 1}/{uuids.length}
            </div>
            <Button onClick={handleForward} variant="ghost" size="icon" className="ml-1" aria-label="Next UUID">
              <ChevronRight className="h-4 w-4" />
            </Button>
          </motion.div>
        </AnimatePresence>
      )}
    </div>
  );
}

Dependencies

import React, { useCallback, useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { ChevronLeft, ChevronRight } from 'lucide-react'
import { motion, AnimatePresence } from 'framer-motion'

Components Used

Notes

  • Uses Framer Motion for animations
  • Implements circular navigation
  • Manages highlight state
  • Handles UUID selection
  • Provides progress tracking
  • Supports keyboard navigation
  • Uses callback optimization
  • Maintains internal state
  • Implements error boundaries
  • Ensures accessibility

Pages/Components Referenced By