mirror of
https://github.com/harivansh-afk/Austens-Wedding-Guide.git
synced 2026-04-18 11:02:06 +00:00
improved literary analysis pgae and added compare works sub page
This commit is contained in:
parent
0134c81526
commit
5ca37d03f2
13 changed files with 3953 additions and 59 deletions
19
src/components/CharacterDetails.jsx
Normal file
19
src/components/CharacterDetails.jsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// For smaller network visualizations in subpages
|
||||
const subGraphConfig = {
|
||||
nodeRelSize: 4,
|
||||
linkDistance: 80,
|
||||
d3: {
|
||||
alphaDecay: 0.05, // Faster decay for smaller graphs
|
||||
alphaMin: 0.001,
|
||||
velocityDecay: 1
|
||||
},
|
||||
cooldownTicks: 30,
|
||||
cooldownTime: 1500,
|
||||
};
|
||||
|
||||
// Use static positioning for very small networks
|
||||
const staticLayout = {
|
||||
enableNodeDrag: false,
|
||||
staticGraph: true,
|
||||
nodePosition: { x: node => node.initialX, y: node => node.initialY }
|
||||
};
|
||||
132
src/components/InteractiveGraph.jsx
Normal file
132
src/components/InteractiveGraph.jsx
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import { useRef, useCallback, useState, useEffect } from 'react';
|
||||
|
||||
const InteractiveGraph = ({ graphData, onNodeSelect }) => {
|
||||
const graphRef = useRef();
|
||||
const [selectedNode, setSelectedNode] = useState(null);
|
||||
const clickTimeoutRef = useRef(null);
|
||||
const [isProcessingClick, setIsProcessingClick] = useState(false);
|
||||
|
||||
// Cleanup function for the timeout
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (clickTimeoutRef.current) {
|
||||
clearTimeout(clickTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleNodeClick = useCallback((node) => {
|
||||
if (!node || isProcessingClick) return;
|
||||
|
||||
// Prevent multiple clicks
|
||||
setIsProcessingClick(true);
|
||||
|
||||
// Clear any existing timeout
|
||||
if (clickTimeoutRef.current) {
|
||||
clearTimeout(clickTimeoutRef.current);
|
||||
}
|
||||
|
||||
// Set the selected node immediately
|
||||
setSelectedNode(node);
|
||||
onNodeSelect(node);
|
||||
|
||||
// Reset the processing flag after a short delay
|
||||
clickTimeoutRef.current = setTimeout(() => {
|
||||
setIsProcessingClick(false);
|
||||
}, 300); // 300ms debounce
|
||||
}, [onNodeSelect, isProcessingClick]);
|
||||
|
||||
// Calculate static positions based on container size
|
||||
const containerWidth = 600;
|
||||
const containerHeight = 600;
|
||||
|
||||
const graphConfig = {
|
||||
nodeRelSize: 8,
|
||||
nodeVal: 30,
|
||||
width: containerWidth,
|
||||
height: containerHeight,
|
||||
backgroundColor: "#ffffff",
|
||||
nodeColor: node => node.type === 'protagonist' ? "#4CAF50" :
|
||||
node.type === 'antagonist' ? "#f44336" : "#2196F3",
|
||||
|
||||
// Static layout configuration
|
||||
staticGraph: true,
|
||||
nodePosition: node => ({
|
||||
x: containerWidth/2 + Math.cos(node.index * (2 * Math.PI / graphData.nodes.length)) * (containerWidth/3),
|
||||
y: containerHeight/2 + Math.sin(node.index * (2 * Math.PI / graphData.nodes.length)) * (containerHeight/3)
|
||||
}),
|
||||
|
||||
// Interaction settings
|
||||
enableNodeDrag: false,
|
||||
enableZoom: true,
|
||||
minZoom: 0.5,
|
||||
maxZoom: 2.5,
|
||||
cooldownTime: 0, // Disable force simulation cooldown
|
||||
warmupTicks: 0, // Disable initial ticks
|
||||
|
||||
// Node appearance
|
||||
nodeResolution: 16,
|
||||
|
||||
// Completely disable all tooltips/labels
|
||||
nodeLabel: null,
|
||||
nodeCanvasObject: null,
|
||||
nodeCanvasObjectMode: null,
|
||||
enableNodeHover: false,
|
||||
enableLinkHover: false,
|
||||
|
||||
// Link appearance
|
||||
linkWidth: 2,
|
||||
linkColor: () => "#999999",
|
||||
linkOpacity: 0.6,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
width: '100%',
|
||||
maxWidth: '1000px',
|
||||
margin: '0 auto'
|
||||
}}>
|
||||
<div style={{
|
||||
width: containerWidth,
|
||||
height: containerHeight,
|
||||
border: '1px solid #eee',
|
||||
position: 'relative'
|
||||
}}>
|
||||
<ForceGraph
|
||||
{...graphConfig}
|
||||
ref={graphRef}
|
||||
graphData={graphData}
|
||||
onNodeClick={handleNodeClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right side popup */}
|
||||
<div style={{
|
||||
width: '300px',
|
||||
padding: '20px',
|
||||
backgroundColor: '#fff',
|
||||
border: '1px solid #eee',
|
||||
borderRadius: '4px',
|
||||
height: 'fit-content',
|
||||
visibility: selectedNode ? 'visible' : 'hidden',
|
||||
opacity: selectedNode ? 1 : 0,
|
||||
transition: 'opacity 0.2s ease-in-out'
|
||||
}}>
|
||||
{selectedNode && (
|
||||
<>
|
||||
<h3 style={{ margin: '0 0 10px 0' }}>{selectedNode.name}</h3>
|
||||
<p style={{ margin: '0 0 10px 0' }}>Type: {selectedNode.type}</p>
|
||||
<p style={{ margin: '0 0 10px 0' }}>Novel: {selectedNode.novel}</p>
|
||||
{selectedNode.class && (
|
||||
<p style={{ margin: '0 0 10px 0' }}>Class: {selectedNode.class}</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InteractiveGraph;
|
||||
|
|
@ -30,12 +30,20 @@ const Navbar = () => {
|
|||
<Link to="/market-calculator" className="text-sage-700 hover:text-sage-900 transition-colors">
|
||||
Market Value
|
||||
</Link>
|
||||
<Link
|
||||
to="/analysis"
|
||||
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
|
||||
>
|
||||
Literary Analysis
|
||||
</Link>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Link
|
||||
to="/analysis"
|
||||
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
|
||||
>
|
||||
Literary Analysis
|
||||
</Link>
|
||||
<Link
|
||||
to="/network"
|
||||
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
|
||||
>
|
||||
Character Network
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@ import Navbar from '../Navbar';
|
|||
|
||||
const MainLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className="min-h-screen bg-cream-50">
|
||||
<div className="min-h-screen bg-cream-50 flex flex-col">
|
||||
<Navbar />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{children}
|
||||
<main className="flex-1 flex flex-col">
|
||||
<div className="flex-1 container mx-auto px-4 sm:px-6 lg:px-8 py-8 flex flex-col">
|
||||
<div className="flex-1 flex flex-col">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className="bg-sage-100 border-t border-sage-200 mt-12">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<footer className="bg-sage-100 border-t border-sage-200">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="text-center font-cormorant text-sage-900">
|
||||
<p className="text-lg">"It is a truth universally acknowledged..."</p>
|
||||
<p className="mt-2">© {new Date().getFullYear()} Austen's Wedding Guide</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue