mirror of
https://github.com/SamyRai/tercul-frontend.git
synced 2025-12-27 00:11:35 +00:00
- Enhanced annotation system with improved inline editing - Updated author components with new card and header designs - Improved reading view with enhanced line numbering and controls - Added new blog management features and tag management - Updated UI components with improved accessibility and styling - Enhanced search functionality with better filtering - Added new dashboard features and activity feeds - Improved translation selector and work comparison tools - Updated GraphQL integration and API hooks - Enhanced responsive design and mobile experience
121 lines
3.2 KiB
TypeScript
121 lines
3.2 KiB
TypeScript
import { Copy } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
|
|
interface LineNumberedTextProps {
|
|
content: string;
|
|
fontSizeClass?: string;
|
|
onLineClick?: (lineNumber: number) => void;
|
|
highlightedLine?: number;
|
|
}
|
|
|
|
export function LineNumberedText({
|
|
content,
|
|
fontSizeClass = "text-size-md",
|
|
onLineClick,
|
|
highlightedLine,
|
|
}: LineNumberedTextProps) {
|
|
const { toast } = useToast();
|
|
const [hoveredLine, setHoveredLine] = useState<number | null>(null);
|
|
|
|
// Split content into lines
|
|
const lines = content.split("\n");
|
|
|
|
const handleLineHover = (lineNumber: number) => {
|
|
setHoveredLine(lineNumber);
|
|
};
|
|
|
|
const handleLineLeave = () => {
|
|
setHoveredLine(null);
|
|
};
|
|
|
|
const handleCopyLine = (_lineNumber: number, lineText: string) => {
|
|
navigator.clipboard.writeText(lineText);
|
|
toast({
|
|
description: "Line copied to clipboard",
|
|
duration: 2000,
|
|
});
|
|
};
|
|
|
|
const handleCopyLineLink = (lineNumber: number) => {
|
|
const url = new URL(window.location.href);
|
|
url.hash = `line-${lineNumber}`;
|
|
navigator.clipboard.writeText(url.toString());
|
|
toast({
|
|
description: "Link to line copied to clipboard",
|
|
duration: 2000,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className={`reading-text ${fontSizeClass}`}>
|
|
{lines.map((line, index) => {
|
|
const lineNumber = index + 1;
|
|
const isHighlighted = lineNumber === highlightedLine;
|
|
const isHovered = lineNumber === hoveredLine;
|
|
|
|
return (
|
|
<div
|
|
key={`line-${lineNumber}`}
|
|
id={`line-${lineNumber}`}
|
|
className={`text-line group ${
|
|
isHighlighted
|
|
? "bg-navy/10 dark:bg-cream/10"
|
|
: "hover:bg-navy/5 dark:hover:bg-cream/5"
|
|
} py-1 rounded flex`}
|
|
onMouseEnter={() => handleLineHover(lineNumber)}
|
|
onMouseLeave={handleLineLeave}
|
|
onClick={() => onLineClick?.(lineNumber)}
|
|
>
|
|
<span className="line-number">{lineNumber}</span>
|
|
<p className="flex-1">{line}</p>
|
|
|
|
{/* Copy buttons that appear on hover */}
|
|
{isHovered && (
|
|
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity ml-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-6 w-6"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleCopyLine(lineNumber, line);
|
|
}}
|
|
>
|
|
<Copy className="h-4 w-4" />
|
|
<span className="sr-only">Copy line</span>
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-6 w-6"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleCopyLineLink(lineNumber);
|
|
}}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4"
|
|
>
|
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
</svg>
|
|
<span className="sr-only">Copy link to line</span>
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|