1+ import React , { useEffect , useRef , useState } from 'react' ;
2+ import { useLanguage } from '../i18n/languageContext' ;
3+
4+ interface SensorComparisonProps {
5+ selectedSensors : string [ ] ;
6+ }
7+
8+ // Sensor dimensions data
9+ const sensorDimensions : Record < string , { width : number ; height : number ; name : string } > = {
10+ 'medium-format' : { width : 53 , height : 40 , name : 'Medium Format' } ,
11+ 'full-frame' : { width : 36 , height : 24 , name : 'Full Frame (35mm)' } ,
12+ 'aps-c' : { width : 23.6 , height : 15.6 , name : 'APS-C' } ,
13+ 'm43' : { width : 17.3 , height : 13 , name : 'M43' } ,
14+ '1-inch' : { width : 13.2 , height : 8.8 , name : '1 inch' } ,
15+ '2-3-inch' : { width : 8.8 , height : 6.6 , name : '2/3 inch' } ,
16+ '1-1.7-inch' : { width : 7.6 , height : 5.7 , name : '1/1.7 inch' } ,
17+ '1-2.3-inch' : { width : 6.3 , height : 4.7 , name : '1/2.3 inch' } ,
18+ '1-3-inch' : { width : 4.8 , height : 3.6 , name : '1/3 inch' }
19+ } ;
20+
21+ const SensorComparison : React . FC < SensorComparisonProps > = ( { selectedSensors } ) => {
22+ const { t, language } = useLanguage ( ) ;
23+ const [ isStacked , setIsStacked ] = useState ( false ) ;
24+ const [ containerWidth , setContainerWidth ] = useState ( 0 ) ;
25+ const containerRef = useRef < HTMLDivElement > ( null ) ;
26+
27+ // Update container width on mount and resize
28+ useEffect ( ( ) => {
29+ const updateWidth = ( ) => {
30+ if ( containerRef . current ) {
31+ setContainerWidth ( containerRef . current . offsetWidth - 48 ) ; // Subtract padding
32+ }
33+ } ;
34+
35+ updateWidth ( ) ;
36+ window . addEventListener ( 'resize' , updateWidth ) ;
37+ return ( ) => window . removeEventListener ( 'resize' , updateWidth ) ;
38+ } , [ ] ) ;
39+
40+ // Function to calculate scaled dimensions for display
41+ const calculateDimensions = ( sensorKey : string ) : { width : number ; height : number } => {
42+ const dimensions = sensorDimensions [ sensorKey ] ;
43+ const maxSensorWidth = sensorDimensions [ 'medium-format' ] . width ; // Width of medium format sensor
44+
45+ // Calculate scale factor based on container width
46+ // If container is smaller than medium format sensor, use container width as reference
47+ const baseWidth = Math . min ( containerWidth , 600 ) ; // Limit max width to 600px
48+ const scaleFactor = baseWidth / maxSensorWidth ;
49+
50+ // Apply the scale factor to maintain relative sizes
51+ const width = dimensions . width * scaleFactor ;
52+ const height = dimensions . height * scaleFactor ;
53+
54+ return { width, height } ;
55+ } ;
56+
57+ // Always sort sensors by size for consistent ordering
58+ const sortedSensors = [ ...selectedSensors ] . sort ( ( a , b ) =>
59+ ( sensorDimensions [ b ] . width * sensorDimensions [ b ] . height ) -
60+ ( sensorDimensions [ a ] . width * sensorDimensions [ a ] . height )
61+ ) ;
62+
63+ // Find dimensions of largest selected sensor for container sizing
64+ let maxWidth = 0 ;
65+ let maxHeight = 0 ;
66+
67+ selectedSensors . forEach ( sensor => {
68+ const { width, height } = calculateDimensions ( sensor ) ;
69+ maxWidth = Math . max ( maxWidth , width ) ;
70+ maxHeight = Math . max ( maxHeight , height ) ;
71+ } ) ;
72+
73+ return (
74+ < div className = "p-6" ref = { containerRef } >
75+ < div className = "flex justify-between items-center mb-6" >
76+ < h2 className = "text-xl font-semibold text-gray-800" > { t ( 'sensor.title' ) } </ h2 >
77+ < div className = "flex items-center" >
78+ < span className = "mr-2 text-sm text-gray-600" > { t ( 'sensor.stack' ) } </ span >
79+ < label className = "inline-flex items-center cursor-pointer" >
80+ < input
81+ type = "checkbox"
82+ className = "sr-only peer"
83+ checked = { isStacked }
84+ onChange = { ( ) => setIsStacked ( ! isStacked ) }
85+ />
86+ < div className = "relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-100 dark:peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:translate-x-[-100%] peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-500" > </ div >
87+ </ label >
88+ </ div >
89+ </ div >
90+
91+ { isStacked ? (
92+ // Stacked view - sensors overlay each other with centers aligned
93+ < div className = "relative mx-auto my-10 overflow-hidden" >
94+ < div
95+ className = "relative"
96+ style = { {
97+ width : `${ maxWidth } px` ,
98+ height : `${ maxHeight } px` ,
99+ minHeight : '200px' ,
100+ maxWidth : '100%' ,
101+ margin : '0 auto'
102+ } }
103+ >
104+ { sortedSensors . map ( ( sensor , index ) => {
105+ const { width, height } = calculateDimensions ( sensor ) ;
106+ const actualDimensions = sensorDimensions [ sensor ] ;
107+
108+ // Calculate positioning to center this block relative to container
109+ const leftOffset = ( maxWidth - width ) / 2 ;
110+ const topOffset = ( maxHeight - height ) / 2 ;
111+ const zIndex = index + 1 ;
112+
113+ return (
114+ < div
115+ key = { sensor }
116+ className = "absolute transform-gpu"
117+ style = { {
118+ left : `${ leftOffset } px` ,
119+ top : `${ topOffset } px` ,
120+ zIndex,
121+ } }
122+ >
123+ < div
124+ className = "sensor-block bg-blue-50 hover:bg-blue-100 relative border-2 border-gray-400"
125+ style = { {
126+ width : `${ width } px` ,
127+ height : `${ height } px` ,
128+ minWidth : '40px' ,
129+ minHeight : '30px' ,
130+ } }
131+ >
132+ < div className = "absolute bottom-0 left-0 right-0 bg-white bg-opacity-75 text-gray-700 text-xs py-1 px-2" >
133+ { actualDimensions . name } ({ actualDimensions . width } ×{ actualDimensions . height } mm)
134+ </ div >
135+ </ div >
136+ </ div >
137+ ) ;
138+ } ) }
139+ </ div >
140+ </ div >
141+ ) : (
142+ // Regular view - sensors are stacked vertically with smaller on top
143+ < div className = "flex flex-col items-center justify-center space-y-8 py-4" >
144+ { [ ...sortedSensors ] . reverse ( ) . map ( sensor => {
145+ const { width, height } = calculateDimensions ( sensor ) ;
146+ const actualDimensions = sensorDimensions [ sensor ] ;
147+
148+ return (
149+ < div key = { sensor } className = "flex flex-col items-center mb-8 w-full" >
150+ < div
151+ className = "sensor-block bg-blue-50 hover:bg-blue-100 mb-2 relative"
152+ style = { {
153+ width : `${ width } px` ,
154+ height : `${ height } px` ,
155+ minWidth : '40px' ,
156+ minHeight : '30px' ,
157+ maxWidth : '100%'
158+ } }
159+ >
160+ < div className = "text-xs md:text-sm font-medium absolute top-2 left-2 bg-white bg-opacity-75 px-2 py-1 rounded" >
161+ { actualDimensions . name }
162+ </ div >
163+ </ div >
164+ < div className = "text-sm text-gray-500" >
165+ { actualDimensions . width } ×{ actualDimensions . height } mm
166+ </ div >
167+ < div className = "text-xs text-gray-400 mt-1" >
168+ ({ ( actualDimensions . width * actualDimensions . height ) . toFixed ( 1 ) } mm²)
169+ </ div >
170+ </ div >
171+ ) ;
172+ } ) }
173+ </ div >
174+ ) }
175+
176+ < div className = "mt-8 p-4 bg-gray-50 rounded-lg" >
177+ < h3 className = "text-md font-medium text-gray-700 mb-2" > { t ( 'sensor.about.title' ) } </ h3 >
178+ < p className = "text-sm text-gray-600" >
179+ { t ( 'sensor.about.description' ) }
180+ </ p >
181+ </ div >
182+ </ div >
183+ ) ;
184+ } ;
185+
186+ export default SensorComparison ;
0 commit comments