Skip to content

Commit 6302b1e

Browse files
committed
fix(sketcher): fix some sketcher issues by forcing only one ketcher instance to be in use at once
1 parent b6a710c commit 6302b1e

File tree

7 files changed

+159
-84
lines changed

7 files changed

+159
-84
lines changed

components/SMILESInput.tsx

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { captureException } from "@sentry/nextjs";
66
import dynamic from "next/dynamic";
77

88
import { useEnqueueError } from "../hooks/useEnqueueStackError";
9+
import { useIsASketcherOpen } from "../state/sketcherState";
910
import { getErrorMessage } from "../utils/next/orvalError";
1011
import { CenterLoader } from "./CenterLoader";
1112
import type { SketcherProps } from "./Sketcher";
@@ -30,7 +31,15 @@ export interface SMILESInputProps {
3031
* called when the delete button is clicked
3132
* @returns nothing
3233
*/
33-
onDelete: () => void;
34+
onDelete?: () => void;
35+
/**
36+
* Called when the sketcher opens
37+
*/
38+
onOpen?: () => void;
39+
/**
40+
* Called when the sketcher closes
41+
*/
42+
onClose?: () => void;
3443
/**
3544
* whether the sketcher is displayed by default or not
3645
*/
@@ -43,6 +52,10 @@ export interface SMILESInputProps {
4352
* Height of sketcher canvas
4453
*/
4554
height?: string | number;
55+
/**
56+
* Whether the edit button to enable the sketcher should be disabled
57+
*/
58+
sketcherDisabled: boolean;
4659
}
4760

4861
/**
@@ -55,14 +68,19 @@ export const SMILESInput = ({
5568
initialMode = "smiles",
5669
width = "700px",
5770
height = "500px",
71+
sketcherDisabled,
5872
onSave,
5973
onDelete,
74+
onOpen,
75+
onClose,
6076
}: SMILESInputProps) => {
6177
const { enqueueError } = useEnqueueError();
6278

6379
const [smiles, setSmiles] = useState(value);
6480
const [mode, setMode] = useState(initialMode);
6581

82+
const [, setIsASketcherOpen] = useIsASketcherOpen();
83+
6684
// Synchronise the controlled prop to the uncontrolled state
6785
useEffect(() => {
6886
setSmiles(value);
@@ -71,29 +89,56 @@ export const SMILESInput = ({
7189
if (mode === "smiles") {
7290
return (
7391
<>
74-
<IconButton sx={{ mr: 1 }} onClick={onDelete}>
75-
<DeleteForeverIcon />
76-
</IconButton>
77-
<TextField label="SMILES" value={smiles} onChange={(event) => onSave(event.target.value)} />
78-
<Tooltip title="Use a molecule sketcher">
79-
<IconButton sx={{ ml: 1 }} onClick={() => setMode("sketcher")}>
80-
<EditIcon />
92+
<Tooltip title="Delete this molecule">
93+
<IconButton sx={{ mr: 1 }} onClick={onDelete}>
94+
<DeleteForeverIcon />
8195
</IconButton>
8296
</Tooltip>
97+
<TextField label="SMILES" value={smiles} onChange={(event) => onSave(event.target.value)} />
98+
<Tooltip
99+
title={
100+
sketcherDisabled ? "Only one sketcher may be used at once" : "Use a molecule sketcher"
101+
}
102+
>
103+
<span>
104+
<IconButton
105+
disabled={sketcherDisabled}
106+
sx={{ ml: 1 }}
107+
onClick={() => {
108+
setMode("sketcher");
109+
onOpen && onOpen();
110+
}}
111+
>
112+
<EditIcon />
113+
</IconButton>
114+
</span>
115+
</Tooltip>
83116
</>
84117
);
85118
}
86119

87120
return (
88121
<Box display="flex" flexDirection="column" gap={1} width={width}>
89122
<Box height={height}>
90-
<Sketcher smiles={smiles} />
123+
<Sketcher smiles={smiles} onUnmount={() => setIsASketcherOpen(false)} />
91124
</Box>
92125
<ButtonGroup size="small" sx={{ alignSelf: "end" }} variant="outlined">
93-
<Button color="warning" onClick={onDelete}>
126+
<Button
127+
color="warning"
128+
onClick={() => {
129+
onDelete && onDelete();
130+
onClose && onClose();
131+
}}
132+
>
94133
Delete
95134
</Button>
96-
<Button color="info" onClick={() => setMode("smiles")}>
135+
<Button
136+
color="info"
137+
onClick={() => {
138+
setMode("smiles");
139+
onClose && onClose();
140+
}}
141+
>
97142
Cancel
98143
</Button>
99144
<Button
@@ -106,6 +151,7 @@ export const SMILESInput = ({
106151
setMode("smiles");
107152
onSave(smi);
108153
global.ketcher = undefined;
154+
onClose && onClose();
109155
} else {
110156
enqueueError("Smiles not obtained");
111157
}

components/Sketcher.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import "ketcher-react/dist/index.css";
1010

1111
export interface SketcherProps {
1212
smiles: string;
13+
onUnmount?: () => void;
1314
}
1415

1516
export const allButtons = {
@@ -74,14 +75,22 @@ export const allButtons = {
7475
"enhanced-stereo": false,
7576
};
7677

77-
export const Sketcher = ({ smiles }: SketcherProps) => {
78+
export const Sketcher = ({ smiles, onUnmount }: SketcherProps) => {
7879
const { enqueueError } = useEnqueueError();
7980

8081
// Synchronise the react state to the component
8182
useEffect(() => {
8283
global.ketcher?.setMolecule(smiles);
8384
}, [smiles]);
8485

86+
useEffect(() => {
87+
// do nothing on mount
88+
return () => {
89+
onUnmount && onUnmount();
90+
global.ketcher = undefined;
91+
};
92+
});
93+
8594
return (
8695
<Editor
8796
buttons={Object.fromEntries(

components/executionsCards/JobCard/MultipleMoleculeInput.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
Radio,
1010
RadioGroup,
1111
} from "@mui/material";
12+
import { nanoid } from "nanoid";
1213

14+
import { useIsASketcherOpen } from "../../../state/sketcherState";
1315
import type { FILE_PROTOCOL } from "../../../utils/app/urls";
1416
import { addFileProtocol, removeFileProtocol } from "../../../utils/app/urls";
1517
import type { FileSelection, SharedProps } from "../../FileSelector";
@@ -58,16 +60,16 @@ export const MultipleMoleculeInput = ({
5860
onFileSelect,
5961
reset,
6062
}: MultipleMoleculeInputProps) => {
61-
const id = useRef(Math.floor(Math.random() * 1000)).current;
63+
const uuid = useRef(nanoid()).current;
6264
const [inputMethod, setInputMethod] = useState<InputMethod>("smiles");
6365

6466
return (
6567
<>
6668
<FormControl>
67-
<FormLabel id={`input-method-${id}`}>Method</FormLabel>
69+
<FormLabel id={`input-method-${uuid}`}>Method</FormLabel>
6870
<RadioGroup
6971
row
70-
aria-labelledby={`input-method-${id}`}
72+
aria-labelledby={`input-method-${uuid}`}
7173
value={inputMethod}
7274
onChange={(_event, value) => {
7375
reset();
@@ -109,6 +111,8 @@ export const SketcherInputs = ({ value, onMoleculesChange }: SketcherInputsProps
109111
value = value.join("\n");
110112
}
111113

114+
const [sketcherDisabled, setIsASketcherOpen] = useIsASketcherOpen();
115+
112116
// Then split it into an array
113117
const valueArray = value.split("\n");
114118

@@ -117,16 +121,20 @@ export const SketcherInputs = ({ value, onMoleculesChange }: SketcherInputsProps
117121
{valueArray.map((smiles, index) => (
118122
<Box key={index} mb={2}>
119123
<SMILESInput
124+
sketcherDisabled={sketcherDisabled}
120125
value={smiles}
121126
onDelete={() => {
122127
const newValue = [...valueArray];
123128
newValue.splice(index, 1);
124129
onMoleculesChange(newValue);
130+
setIsASketcherOpen(false);
125131
}}
132+
onOpen={() => setIsASketcherOpen(true)}
126133
onSave={(smi) => {
127134
const newValue = [...valueArray];
128135
newValue[index] = smi;
129136
onMoleculesChange(newValue);
137+
setIsASketcherOpen(false);
130138
}}
131139
/>
132140
</Box>

components/instances/JobDetails/JobInputSection/JobInputSection.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,10 @@ export const JobInputSection = ({ instance }: JobInputSectionProps) => {
4848
{inputs.map((input) => {
4949
const isFile = input.value.map((val) => val.startsWith(FILE_PROTOCOL)).some((v) => v);
5050
const moleculesType = input.type === "molecules" || input.type === "molecule";
51-
5251
let value = input.value;
5352
if (moleculesType && isFile) {
5453
value = value.map(removeFileProtocol);
5554
}
56-
5755
return (
5856
<ListItem key={input.name} sx={{ alignItems: "flex-start" }}>
5957
<ListItemAvatar>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"ketcher-standalone": "2.7.2",
8282
"lodash-es": "4.17.21",
8383
"material-ui-popup-state": "4.1.0",
84+
"nanoid": "^4.0.1",
8485
"next": "13.1.6",
8586
"next-http-proxy-middleware": "1.2.5",
8687
"nextjs-routes": "1.0.8",

0 commit comments

Comments
 (0)