@@ -7,12 +7,46 @@ import { LabwareDetailItem } from './LabwareDetailItem';
77import { LABWARE_METADATA } from './labwareMetadata' ;
88import { LabwareConfig , LabwareKey , TecanLabwares } from './types' ;
99
10- function getGridArea ( key : LabwareKey ) : string {
11- if ( key === 'destPcr1' || key === 'destPcr2' ) {
12- return 'rightColumn' ;
13- }
14- return key ;
15- }
10+ // Responsive scaling constants
11+ const GRID_PADDING = 8 ; // Padding around the grid container (from style.padding)
12+ const GRID_GAP = 8 ; // Gap between grid cells (from style.gap)
13+ const COLUMN_COUNT = 5 ; // Number of columns in the grid
14+ const GRID_OVERHEAD = GRID_PADDING * 2 + GRID_GAP * ( COLUMN_COUNT - 1 ) ; // Total space used by padding and gaps
15+ const COLUMN_OVERHEAD = 60 ; // Estimated width per column for borders and padding
16+ const BASE_CONTENT_WIDTH = 1400 ; // Designed content width at 1.0 scale factor
17+ const SCALE_SAFETY_MARGIN = 0.95 ; // 5% safety margin to prevent overflow
18+
19+ // Static styles
20+ const CONTAINER_STYLE = {
21+ width : '100%' ,
22+ } as const ;
23+
24+ const GRID_CONTAINER_BASE_STYLE = {
25+ display : 'grid' ,
26+ gridTemplateColumns : 'auto auto auto auto auto' ,
27+ gap : '8px' ,
28+ gridTemplateAreas : `
29+ "mmPlate aPlate nemoDilution destPcr rightColumn"
30+ "mmPlate bPlate nemoDestPcr2 destLc rightColumn"
31+ "mmPlate nemoWater nemoDestTaqMan fluidX rightColumn"
32+ ` ,
33+ padding : '8px' ,
34+ backgroundColor : PALETTE . gray1 ,
35+ borderRadius : '4px' ,
36+ width : 'fit-content' ,
37+ } as const ;
38+
39+ const LABWARE_ITEM_BASE_STYLE = {
40+ justifySelf : 'center' ,
41+ } as const ;
42+
43+ const RIGHT_COLUMN_STYLE = {
44+ gridArea : 'rightColumn' ,
45+ display : 'grid' ,
46+ gridTemplateRows : 'auto auto' ,
47+ alignSelf : 'center' ,
48+ gap : '8px' ,
49+ } as const ;
1650
1751export function TecanDeckView ( { labwares } : { labwares : TecanLabwares } ) {
1852 const containerRef = React . useRef < HTMLDivElement > ( null ) ;
@@ -44,48 +78,54 @@ export function TecanDeckView({ labwares }: { labwares: TecanLabwares }) {
4478 return ;
4579 }
4680
47- const gridOverhead = 16 + 32 ;
48- const itemOverheadPerColumn = 60 ;
49- const totalItemOverhead = itemOverheadPerColumn * 5 ;
50- const baseContentWidth = 1400 ;
81+ const totalColumnOverhead = COLUMN_OVERHEAD * COLUMN_COUNT ;
5182 const availableContentWidth =
52- ( availableWidth - gridOverhead - totalItemOverhead ) * 0.95 ;
83+ ( availableWidth - GRID_OVERHEAD - totalColumnOverhead ) *
84+ SCALE_SAFETY_MARGIN ;
5385
5486 const calculatedScale = Math . max (
5587 0.1 ,
56- Math . min ( 1.0 , availableContentWidth / baseContentWidth ) ,
88+ Math . min ( 1.0 , availableContentWidth / BASE_CONTENT_WIDTH ) ,
5789 ) ;
5890 setScaleFactor ( calculatedScale ) ;
5991 } , [ availableWidth ] ) ;
6092
61- const allLabwareKeys = Object . keys ( LABWARE_METADATA ) as Array < LabwareKey > ;
62- const allLabwares : Array < LabwareConfig > = allLabwareKeys . map ( ( key ) => ( {
63- key,
64- ...LABWARE_METADATA [ key ] ,
65- content : labwares [ key ] ?. content ,
66- } ) ) ;
93+ const allLabwares : Array < LabwareConfig > = React . useMemo ( ( ) => {
94+ const allLabwareKeys = Object . keys ( LABWARE_METADATA ) as Array < LabwareKey > ;
95+ return allLabwareKeys . map ( ( key ) => ( {
96+ key,
97+ ...LABWARE_METADATA [ key ] ,
98+ content : labwares [ key ] ?. content ,
99+ } ) ) ;
100+ } , [ labwares ] ) ;
67101
68102 const isRightColumnAndNeedContainer = ( key : LabwareKey ) =>
69103 key === 'destPcr1' || key === 'destPcr2' ;
70104
71- const ROW_1_KEYS = new Set < LabwareKey > ( [
72- 'aPlate' ,
73- 'nemoDilution' ,
74- 'destPcr' ,
75- 'destPcr1' ,
76- 'destPcr2' ,
77- ] ) ;
78- const ROW_2_KEYS = new Set < LabwareKey > ( [ 'bPlate' , 'nemoDestPcr2' , 'destLc' ] ) ;
79- const ROW_3_KEYS = new Set < LabwareKey > ( [
80- 'nemoWater' ,
81- 'nemoDestTaqMan' ,
82- 'fluidX' ,
83- ] ) ;
105+ const ROWS = [
106+ {
107+ keys : new Set < LabwareKey > ( [
108+ 'aPlate' ,
109+ 'nemoDilution' ,
110+ 'destPcr' ,
111+ 'destPcr1' ,
112+ 'destPcr2' ,
113+ ] ) ,
114+ alignment : 'start' as const ,
115+ } ,
116+ {
117+ keys : new Set < LabwareKey > ( [ 'bPlate' , 'nemoDestPcr2' , 'destLc' ] ) ,
118+ alignment : 'center' as const ,
119+ } ,
120+ {
121+ keys : new Set < LabwareKey > ( [ 'nemoWater' , 'nemoDestTaqMan' , 'fluidX' ] ) ,
122+ alignment : 'end' as const ,
123+ } ,
124+ ] ;
84125
85126 const getVerticalAlignment = ( key : LabwareKey ) : string => {
86- if ( ROW_1_KEYS . has ( key ) ) return 'start' ;
87- if ( ROW_3_KEYS . has ( key ) ) return 'end' ;
88- return 'center' ;
127+ const row = ROWS . find ( ( r ) => r . keys . has ( key ) ) ;
128+ return row ?. alignment ?? 'center' ;
89129 } ;
90130
91131 const renderLabware = ( labware : LabwareConfig ) =>
@@ -104,41 +144,19 @@ export function TecanDeckView({ labwares }: { labwares: TecanLabwares }) {
104144 const isRowEmpty = ( rowKeys : Set < LabwareKey > ) : boolean =>
105145 Array . from ( rowKeys ) . every ( ( key ) => ! labwares [ key ] ?. content ) ;
106146
107- const row1Empty = isRowEmpty ( ROW_1_KEYS ) ;
108- const row2Empty = isRowEmpty ( ROW_2_KEYS ) ;
109- const row3Empty = isRowEmpty ( ROW_3_KEYS ) ;
110-
111147 const EMPTY_ROW_SIZE = '0.3fr' ;
112148 const CONTENT_ROW_SIZE = '1fr' ;
113149
114- const gridTemplateRows = [
115- row1Empty ? EMPTY_ROW_SIZE : CONTENT_ROW_SIZE ,
116- row2Empty ? EMPTY_ROW_SIZE : CONTENT_ROW_SIZE ,
117- row3Empty ? EMPTY_ROW_SIZE : CONTENT_ROW_SIZE ,
118- ] . join ( ' ' ) ;
150+ const gridTemplateRows = ROWS . map ( ( row ) =>
151+ isRowEmpty ( row . keys ) ? EMPTY_ROW_SIZE : CONTENT_ROW_SIZE ,
152+ ) . join ( ' ' ) ;
119153
120154 return (
121- < div
122- ref = { containerRef }
123- style = { {
124- width : '100%' ,
125- } }
126- >
155+ < div ref = { containerRef } style = { CONTAINER_STYLE } >
127156 < div
128157 style = { {
129- display : 'grid' ,
130- gridTemplateColumns : 'auto auto auto auto auto' ,
158+ ...GRID_CONTAINER_BASE_STYLE ,
131159 gridTemplateRows,
132- gap : '8px' ,
133- gridTemplateAreas : `
134- "mmPlate aPlate nemoDilution destPcr rightColumn"
135- "mmPlate bPlate nemoDestPcr2 destLc rightColumn"
136- "mmPlate nemoWater nemoDestTaqMan fluidX rightColumn"
137- ` ,
138- padding : '8px' ,
139- backgroundColor : PALETTE . gray1 ,
140- borderRadius : '4px' ,
141- width : 'fit-content' ,
142160 } }
143161 >
144162 { allLabwares
@@ -147,24 +165,16 @@ export function TecanDeckView({ labwares }: { labwares: TecanLabwares }) {
147165 < div
148166 key = { labware . key }
149167 style = { {
150- gridArea : getGridArea ( labware . key ) ,
168+ ...LABWARE_ITEM_BASE_STYLE ,
169+ gridArea : labware . key ,
151170 alignSelf : getVerticalAlignment ( labware . key ) ,
152- justifySelf : 'center' ,
153171 } }
154172 >
155173 { renderLabware ( labware ) }
156174 </ div >
157175 ) ) }
158176
159- < div
160- style = { {
161- gridArea : 'rightColumn' ,
162- display : 'grid' ,
163- gridTemplateRows : 'auto auto' ,
164- alignSelf : 'center' ,
165- gap : '8px' ,
166- } }
167- >
177+ < div style = { RIGHT_COLUMN_STYLE } >
168178 { allLabwares
169179 . filter ( ( labware ) => isRightColumnAndNeedContainer ( labware . key ) )
170180 . map ( ( labware ) => (
0 commit comments