diff --git a/src/components/SocialClassView.tsx b/src/components/SocialClassView.tsx index cf02559..af1b5a3 100644 --- a/src/components/SocialClassView.tsx +++ b/src/components/SocialClassView.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Box, Typography, Paper, Grid, Tooltip, Card, CardContent, Chip, Dialog, DialogTitle, DialogContent } from '@mui/material'; +import { Box, Typography, Paper, Grid, Tooltip, Card, CardContent, Chip, Dialog, DialogTitle, DialogContent, Divider } from '@mui/material'; import { SocialClass, Character } from '../types/timeline'; const socialClasses: SocialClass[] = [ @@ -124,6 +124,9 @@ export default function SocialClassView() { } else if (selectedCharacter.id !== character.id) { setComparisonCharacter(character); setDialogOpen(true); + } else { + // Deselect if clicking the same character + setSelectedCharacter(null); } }; @@ -133,121 +136,286 @@ export default function SocialClassView() { setComparisonCharacter(null); }; + // Color constants for the gradient + const pyramidColors = { + upper: { + start: '#4A5D52', // darker sage + end: '#6B7F75' // lighter sage + }, + middle: { + start: '#5B6E65', + end: '#7C8F86' + }, + lower: { + start: '#6B7F75', + end: '#8C9F96' + } + }; + return ( - - - Social Class in Austen's Novels - - - {/* Social Pyramid */} - - {socialClasses.map((socialClass, index) => ( - - {socialClass.name} - {socialClass.description} - Income: {socialClass.incomeRange} - - } - arrow - > - - - {socialClass.name} +
+ + {/* Social Pyramid */} + + + + + Social Hierarchy - - - ))} - - {/* Character Grid */} - - Character Examples {selectedCharacter && '(Select another character to compare)'} - - - {characters.map((character) => ( - - handleCharacterClick(character)} - sx={{ - cursor: 'pointer', - transition: 'all 0.2s', - '&:hover': { - transform: 'scale(1.02)', - }, - bgcolor: selectedCharacter?.id === character.id ? 'primary.50' : 'background.paper' - }} - > - - - {character.name} - - - {character.novel} - - - - Annual Income: {character.annualIncome} - - - Modern Equivalent: {character.modernEquivalent} - - - - - ))} + + {socialClasses.map((socialClass, index) => { + const colors = index === 0 ? pyramidColors.upper : + index === 1 ? pyramidColors.middle : + pyramidColors.lower; + return ( + + + {socialClass.name} + + + {socialClass.description} + + +
+ + Income Range: {socialClass.incomeRange} + + + Modern Equivalent: {socialClass.modernEquivalent} + +
+ +
+ + Key Characteristics: + +
    + {socialClass.characteristics.map((char, idx) => ( +
  • {char}
  • + ))} +
+
+
+ } + arrow + placement="right" + classes={{ + tooltip: "bg-white shadow-lg", + arrow: "text-white" + }} + sx={{ + "& .MuiTooltip-tooltip": { + backgroundColor: "white", + color: "inherit", + boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)", + opacity: 1, + maxWidth: "none" + }, + "& .MuiTooltip-arrow": { + color: "white", + "&::before": { + backgroundColor: "white", + boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)" + } + } + }} + > + + + {socialClass.name} + + + + ); + })} + +
+
+
+ + {/* Character Examples */} + + + + + Notable Characters + + {selectedCharacter && ( +
+ + {selectedCharacter.name} selected. + Click another character to compare, or click again to deselect. + +
+ )} +
+ {characters.map((character) => ( + handleCharacterClick(character)} + className={` + border transition-all cursor-pointer + ${selectedCharacter?.id === character.id + ? 'border-sage-500 bg-sage-50 shadow-md transform scale-[1.02]' + : 'border-sage-200 bg-white hover:border-sage-300 hover:shadow-sm hover:bg-sage-50/30' + } + `} + sx={{ + position: 'relative', + '&:after': selectedCharacter?.id === character.id ? { + content: '""', + position: 'absolute', + left: -2, + top: '50%', + transform: 'translateY(-50%)', + width: 4, + height: '70%', + backgroundColor: '#4A5D52', + borderRadius: '0 2px 2px 0' + } : {} + }} + > + + +
+ + {character.name} + + + {character.novel} + +
+ +
+ +
+ Annual Income: + {character.annualIncome} +
+
+ Modern Equivalent: + {character.modernEquivalent} +
+
+
+
+ ))} +
+
+
+
{/* Comparison Dialog */} - - Character Comparison - + + + Character Comparison + + {selectedCharacter && comparisonCharacter && ( - - - {selectedCharacter.name} - Novel: {selectedCharacter.novel} - Class: {selectedCharacter.socialClass} - Income: {selectedCharacter.annualIncome} - Modern: {selectedCharacter.modernEquivalent} - {selectedCharacter.description} - - - {comparisonCharacter.name} - Novel: {comparisonCharacter.novel} - Class: {comparisonCharacter.socialClass} - Income: {comparisonCharacter.annualIncome} - Modern: {comparisonCharacter.modernEquivalent} - {comparisonCharacter.description} - + + {[selectedCharacter, comparisonCharacter].map((char, index) => ( + + + {char.name} + + +
+
+ + Novel + + + {char.novel} + +
+
+ + Annual Income + + + {char.annualIncome} + +
+
+ + Modern Equivalent + + + {char.modernEquivalent} + +
+
+ + Description + + + {char.description} + +
+
+ {index === 0 && ( + + )} +
+ ))}
)}
- +
); } diff --git a/src/pages/NetworkVisualization.tsx b/src/pages/NetworkVisualization.tsx index 58e81ee..68fba83 100644 --- a/src/pages/NetworkVisualization.tsx +++ b/src/pages/NetworkVisualization.tsx @@ -1,9 +1,9 @@ -import React, { useState, useCallback, useRef } from 'react'; +import React, { useState, useCallback, useRef, useEffect } from 'react'; import { characterNetwork } from '../data/character-network'; import { CharacterNode, Relationship, BookNode } from '../types/character-network'; -import { Box, Typography, Paper, Grid, Card, CardContent, IconButton, Tooltip, Popper } from '@mui/material'; +import { Box, Typography, Paper, Grid, Card, CardContent, IconButton, Tooltip, Popper, Chip, Divider, Container, CircularProgress, Fade } from '@mui/material'; import { ForceGraph2D } from 'react-force-graph'; -import { ArrowBack, Help } from '@mui/icons-material'; +import { ArrowBack, Help, ZoomIn, ZoomOut, CenterFocusStrong, Search } from '@mui/icons-material'; import * as d3 from 'd3'; interface NetworkNode { @@ -40,12 +40,40 @@ interface TooltipState { y: number; } +// Proper typing for ForceGraph methods +interface ForceGraphMethods { + zoom: (k: number) => void; + zoomToFit: (durationMs?: number, padding?: number) => void; + d3Force: (forceName: string, force: any) => void; + getZoom: () => number; + centerAt: (x?: number, y?: number, durationMs?: number) => void; +} + export default function NetworkVisualization() { const [selectedNode, setSelectedNode] = useState(null); const [selectedRelationships, setSelectedRelationships] = useState([]); const [selectedBook, setSelectedBook] = useState(null); const [tooltip, setTooltip] = useState({ open: false, content: '', x: 0, y: 0 }); const containerRef = useRef(null); + const graphRef = useRef(); + const [isLoading, setIsLoading] = useState(true); + const [isGraphReady, setIsGraphReady] = useState(false); + + // Add loading effect when data changes + useEffect(() => { + setIsLoading(true); + const timer = setTimeout(() => { + setIsLoading(false); + }, 1000); + return () => clearTimeout(timer); + }, [selectedBook]); + + // Track when graph is ready + useEffect(() => { + if (graphRef.current) { + setIsGraphReady(true); + } + }, [graphRef.current]); const handleNodeHover = useCallback((node: NetworkNode | null) => { if (node) { @@ -173,6 +201,12 @@ export default function NetworkVisualization() { const size = node.val || 5; const fontSize = Math.max(12 / scale, 2); + // Add glow effect for highlighted nodes + if (node.id === selectedNode?.id) { + ctx.shadowColor = node.color || '#fff'; + ctx.shadowBlur = 15; + } + // Draw node ctx.beginPath(); ctx.arc(node.x || 0, node.y || 0, size, 0, 2 * Math.PI); @@ -182,6 +216,10 @@ export default function NetworkVisualization() { ctx.lineWidth = 2; ctx.stroke(); + // Clear shadow effect + ctx.shadowColor = 'transparent'; + ctx.shadowBlur = 0; + // Only draw labels when zoomed in enough or for book nodes if (scale > 0.7 || node.type === 'book') { const labelDistance = size + fontSize; @@ -189,7 +227,7 @@ export default function NetworkVisualization() { const textWidth = ctx.measureText(node.name).width; // Semi-transparent background for better readability - ctx.fillStyle = 'rgba(255, 255, 255, 0.85)'; + ctx.fillStyle = 'rgba(255, 255, 255, 0.95)'; const padding = fontSize * 0.3; const backgroundHeight = fontSize + padding * 2; @@ -206,7 +244,7 @@ export default function NetworkVisualization() { ); // Draw text - ctx.fillStyle = '#000'; + ctx.fillStyle = node.id === selectedNode?.id ? '#1976d2' : '#000'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText( @@ -215,7 +253,7 @@ export default function NetworkVisualization() { (node.y || 0) + labelDistance ); } - }, []); + }, [selectedNode]); // Helper function for drawing rounded rectangles const roundRect = ( @@ -267,171 +305,389 @@ export default function NetworkVisualization() {
); + const handleZoomIn = () => { + if (graphRef.current) { + const currentZoom = graphRef.current.getZoom(); + graphRef.current.zoom(currentZoom * 1.5); + } + }; + + const handleZoomOut = () => { + if (graphRef.current) { + const currentZoom = graphRef.current.getZoom(); + graphRef.current.zoom(currentZoom / 1.5); + } + }; + + const handleCenterGraph = () => { + if (graphRef.current) { + graphRef.current.zoomToFit(400, 50); + } + }; + return ( - - - Character Network - - - - - - - - - - - - {selectedBook && ( - - - - - - )} - + + + - - {!selectedBook ? 'Click a book to explore its characters' : 'Click characters to view relationships'} - - - - renderNodeCanvas(node, ctx, scale)} - linkColor={(link: NetworkLink) => link.color} - linkWidth={2} - nodeRelSize={6} - width={800} - height={700} - cooldownTicks={50} - cooldownTime={3000} - linkDirectionalParticles={2} - linkDirectionalParticleSpeed={0.003} - d3AlphaDecay={0.1} - d3VelocityDecay={0.4} - minZoom={0.5} - maxZoom={4} - dagMode={selectedBook ? undefined : 'radialin'} - dagLevelDistance={100} - enablePanInteraction={true} - enableZoomInteraction={true} - onEngineStop={() => { - if (!selectedBook) { - centerGraph(getGraphData()); - } - }} - onNodeDragEnd={(node: NetworkNode) => { - if (node.x && node.y) { - node.fx = node.x; - node.fy = node.y; - } - }} - warmupTicks={100} - onZoom={() => centerGraph(getGraphData())} - centerAt={[400, 350]} - zoom={2} - enableNodeDrag={true} - enableZoomPanInteraction={true} - /> - - - - - {selectedNode && ( - - - - {selectedNode.name} - - - {selectedNode.type === 'book' ? `Published: ${(selectedNode as BookNode).year}` : selectedNode.novel} - - - {selectedNode.description} - - - {selectedNode.type !== 'book' && ( - <> - - Social Class: {(selectedNode as CharacterNode).class} -
- Character Role: {selectedNode.type} -
- - - Relationships - - {selectedRelationships.map((rel, index) => ( - - - {rel.description} - - - Type: {rel.type} - - - Development: - -
    - {rel.development.map((step, i) => ( -
  • - {step} -
  • - ))} -
-
- ))} - - )} -
-
- )} + Character Network + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + + + `0 0 20px ${theme.palette.divider}` + }} + ref={containerRef} + > + {/* Loading overlay */} + + + + + {selectedBook ? 'Loading character relationships...' : 'Loading books...'} + + + + + {/* Graph container */} + + + link.color} + linkWidth={3} + nodeRelSize={8} + width={800} + height={700} + cooldownTicks={50} + cooldownTime={3000} + linkDirectionalParticles={3} + linkDirectionalParticleWidth={2} + linkDirectionalParticleSpeed={0.004} + d3AlphaDecay={0.1} + d3VelocityDecay={0.4} + minZoom={0.5} + maxZoom={4} + dagMode={selectedBook ? undefined : 'radialin'} + dagLevelDistance={100} + enablePanInteraction={true} + enableZoomInteraction={true} + onEngineStop={() => { + if (!selectedBook) { + centerGraph(getGraphData()); + } + }} + onNodeDragEnd={(node: NetworkNode) => { + if (node.x && node.y) { + node.fx = node.x; + node.fy = node.y; + } + }} + warmupTicks={100} + onZoom={(zoom: number) => { + if (!selectedBook) { + centerGraph(getGraphData()); + } + }} + enableNodeDrag={true} + enableZoomPanInteraction={true} + /> + + + + {/* Controls */} + {!isLoading && ( + <> + {selectedBook && ( + + + + + + )} + + + {!selectedBook ? 'Click a book to explore its characters' : 'Click characters to view relationships'} + + + + )} + + + + `0 0 20px ${theme.palette.divider}`, + position: 'sticky', + top: 24 + }}> + theme.palette.divider, + borderRadius: '4px', + }, + '&::-webkit-scrollbar-thumb:hover': { + background: theme => theme.palette.action.hover, + } + }}> + {selectedNode ? ( + + + {selectedNode.name} + + + + {selectedNode.type !== 'book' && ( + + )} + + + {selectedNode.type === 'book' ? `Published: ${(selectedNode as BookNode).year}` : selectedNode.novel} + + + {selectedNode.description} + + + {selectedNode.type !== 'book' && ( + <> + + + Relationships + + + {selectedRelationships.map((rel, index) => ( + + + {rel.description} + + + + Development: + + + {rel.development.map((step, i) => ( + + + {step} + + + ))} + + + ))} + + + )} + + ) : ( + + + Select a node to view details + + + )} + + + - + - + {tooltip.content} diff --git a/src/pages/SocialClass.tsx b/src/pages/SocialClass.tsx index 12582f1..f006c2c 100644 --- a/src/pages/SocialClass.tsx +++ b/src/pages/SocialClass.tsx @@ -1,194 +1,326 @@ import React from 'react'; -import { Box, Typography, Paper, Grid, Chip, Accordion, AccordionSummary, AccordionDetails } from '@mui/material'; +import { Typography, Paper, Grid, Chip, Accordion, AccordionSummary, AccordionDetails } from '@mui/material'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import SocialClassView from '../components/SocialClassView'; export default function SocialClass() { return ( - +
{/* Header Section */} - - +
+

Social Class in Jane Austen's Works - - +

+

An analysis of how social class shapes character relationships, marriage prospects, and social mobility across Austen's novels and their modern retellings. - - - - - - - - - - {/* Character Analysis Section */} - - - Character Studies Across Class Lines - - - - }> - Pride and Prejudice: The Economics of Marriage - - - - - Upper Class - - Mr. Darcy (£10,000 per year): Represents the pinnacle of landed gentry, whose wealth - allows him to transcend local social barriers. His initial pride stems from his position, but his - character development shows wealth doesn't guarantee happiness or love. - - - Lady Catherine de Bourgh: Embodies the aristocracy's resistance to social mobility, - particularly in her opposition to Darcy and Elizabeth's marriage. Her character critiques the - assumption that high birth equals moral superiority. - - - - Middle & Lower Classes - - The Bennet Family: Despite their genteel status, their precarious financial position - (with the entailed estate) demonstrates the vulnerability of women in the period. Mrs. Bennet's - anxiety about her daughters' marriages stems from real economic concerns. - - - The Servants (via Longbourn): Jo Baker's retelling gives voice to characters like - Sarah and Mrs. Hill, revealing the hidden labor that maintains the genteel lifestyle of the main characters. - - - - - - - - }> - Mansfield Park: Moral Worth vs. Social Status - - - - - The Privileged Circle - - The Bertram Family: Sir Thomas's wealth from colonial enterprises in Antigua - raises questions about the source of aristocratic wealth. His children's poor moral education - despite their privileged upbringing challenges assumptions about class and character. - - - Mary and Henry Crawford: Their London sophistication and wealth mask moral - bankruptcy, contrasting with Fanny's humble virtue. They represent the corruption of urban wealth - versus rural values. - - - - The Dependent Relations - - Fanny Price: Her position as a poor relation taken in by wealthy relatives - highlights the complex dynamics of dependency and gratitude. Her moral strength despite her low - status challenges class-based assumptions about worth. - - - The Price Family in Portsmouth: Their cramped, chaotic household provides a - stark contrast to Mansfield's luxury, highlighting the material realities of class difference - in the period. - - - - - - - - }> - Contemporary Perspectives: Modern Retellings - - - - - Pride by Ibi Zoboi - - Zuri Benitez: Reimagines Elizabeth Bennet as a proud Afro-Latina teenager in - Brooklyn, exploring how gentrification and cultural identity intersect with class in contemporary - America. - - - Darius Darcy: As a wealthy African American teenager, his character explores the - complexities of privilege within modern racial and social contexts, updating Austen's examination - of pride and prejudice. - - - - Longbourn's Legacy - - The Servants' Perspective: Baker's novel reveals the physical labor, limited - opportunities, and complex relationships that supported the genteel world of Pride and Prejudice, - giving voice to historically silenced characters. - - - Class Intersections: Through characters like James Smith, the novel explores how - war, servitude, and social mobility operated for those below stairs, expanding our understanding - of Austen's world. - - - - - - +

+
+ + + + +
+
{/* Interactive View */} - - - Interactive Class Analysis - - + +
+ + Interactive Class Analysis + +
+ + Explore Social Class Differences + +
+
+

How to Compare Characters:

+
+

+ 1. + Select your first character by clicking on any character card below +

+

+ 2. + Choose a second character to initiate the comparison +

+

+ 3. + Examine their social positions, incomes, and circumstances in the comparison view +

+
+
+
+

Suggested Comparisons:

+
    +
  • Mr. Darcy vs. Elizabeth Bennet - Explore the class divide in courtship
  • +
  • The Bennet Family vs. The Servants - Understand the full spectrum of social hierarchy
  • +
  • Upper Class vs. Working Class - See the economic disparities of Regency England
  • +
+
+

+ 💡 Tip: Compare characters from different social classes to understand how wealth, status, and opportunities + varied dramatically in Austen's world. +

+
+
+ +
+
+ + {/* Character Analysis Section */} + +
+ + Character Studies Across Class Lines + + + + } + className="bg-sage-50 hover:bg-sage-100" + > + + Pride and Prejudice: The Economics of Marriage + + + + + + + Upper Class + +
+ + Mr. Darcy (£10,000 per year): Represents the pinnacle of landed gentry, whose wealth + allows him to transcend local social barriers. His initial pride stems from his position, but his + character development shows wealth doesn't guarantee happiness or love. + + + Lady Catherine de Bourgh: Embodies the aristocracy's resistance to social mobility, + particularly in her opposition to Darcy and Elizabeth's marriage. Her character critiques the + assumption that high birth equals moral superiority. + +
+
+ + + Middle & Lower Classes + +
+ + The Bennet Family: Despite their genteel status, their precarious financial position + (with the entailed estate) demonstrates the vulnerability of women in the period. Mrs. Bennet's + anxiety about her daughters' marriages stems from real economic concerns. + + + The Servants (via Longbourn): Jo Baker's retelling gives voice to characters like + Sarah and Mrs. Hill, revealing the hidden labor that maintains the genteel lifestyle of the main characters. + +
+
+
+
+
+ + + } + className="bg-sage-50 hover:bg-sage-100" + > + + Mansfield Park: Moral Worth vs. Social Status + + + + + + + The Privileged Circle + +
+ + The Bertram Family: Sir Thomas's wealth from colonial enterprises in Antigua + raises questions about the source of aristocratic wealth. His children's poor moral education + despite their privileged upbringing challenges assumptions about class and character. + + + Mary and Henry Crawford: Their London sophistication and wealth mask moral + bankruptcy, contrasting with Fanny's humble virtue. They represent the corruption of urban wealth + versus rural values. + +
+
+ + + The Dependent Relations + +
+ + Fanny Price: Her position as a poor relation taken in by wealthy relatives + highlights the complex dynamics of dependency and gratitude. Her moral strength despite her low + status challenges class-based assumptions about worth. + + + The Price Family in Portsmouth: Their cramped, chaotic household provides a + stark contrast to Mansfield's luxury, highlighting the material realities of class difference + in the period. + +
+
+
+
+
+ + + } + className="bg-sage-50 hover:bg-sage-100" + > + + Contemporary Perspectives: Modern Retellings + + + + + + + Pride by Ibi Zoboi + +
+ + Zuri Benitez: Reimagines Elizabeth Bennet as a proud Afro-Latina teenager in + Brooklyn, exploring how gentrification and cultural identity intersect with class in contemporary + America. + + + Darius Darcy: As a wealthy African American teenager, his character explores the + complexities of privilege within modern racial and social contexts, updating Austen's examination + of pride and prejudice. + +
+
+ + + Longbourn's Legacy + +
+ + The Servants' Perspective: Baker's novel reveals the physical labor, limited + opportunities, and complex relationships that supported the genteel world of Pride and Prejudice, + giving voice to historically silenced characters. + + + Class Intersections: Through characters like James Smith, the novel explores how + war, servitude, and social mobility operated for those below stairs, expanding our understanding + of Austen's world. + +
+
+
+
+
+
{/* Critical Analysis */} - - - Critical Insights - - - - - - Economic Realities - - - Austen's precise attention to characters' incomes and property reflects the real economic pressures - that shaped marriage choices and social mobility in the Regency period. Her novels acknowledge both - the practical necessity of financial security and its moral dangers. - - + +
+ + Critical Insights + + + +
+ + Economic Realities + + + Austen's precise attention to characters' incomes and property reflects the real economic pressures + that shaped marriage choices and social mobility in the Regency period. Her novels acknowledge both + the practical necessity of financial security and its moral dangers. + +
+
+ +
+ + Social Mobility + + + Through characters like Emma Woodhouse and Elizabeth Bennet, Austen explores the possibilities and + limitations of social mobility through marriage, education, and moral development, while acknowledging + the rigid class structures of her time. + +
+
+ +
+ + Modern Relevance + + + Contemporary adaptations like Pride and Longbourn demonstrate how Austen's exploration of class, + privilege, and social mobility remains relevant to modern discussions of inequality, opportunity, + and social justice. + +
+
- - - - Social Mobility - - - Through characters like Emma Woodhouse and Elizabeth Bennet, Austen explores the possibilities and - limitations of social mobility through marriage, education, and moral development, while acknowledging - the rigid class structures of her time. - - - - - - - Modern Relevance - - - Contemporary adaptations like Pride and Longbourn demonstrate how Austen's exploration of class, - privilege, and social mobility remains relevant to modern discussions of inequality, opportunity, - and social justice. - - - - +
-
+
); }