Skip to content

Commit 78afd98

Browse files
committed
feat: enhance TecanDeckView with responsive grid layout and styling improvements
1 parent 36fc547 commit 78afd98

1 file changed

Lines changed: 82 additions & 72 deletions

File tree

src/TecanDeckView/TecanDeckView.tsx

Lines changed: 82 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,46 @@ import { LabwareDetailItem } from './LabwareDetailItem';
77
import { LABWARE_METADATA } from './labwareMetadata';
88
import { 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

1751
export 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

Comments
 (0)