Skip to content

Commit 29dd0df

Browse files
527 Implement "Enhanced Stereochemistry" window (#558)
* Change Enhanced Stereo window * Add options "new..." to Enhanced Stereo window * Change style of Enhanced Stereo window * Refactor enhanced stereo window * Add TODO comment * Move default StereoGroup number to constants
1 parent b6448d2 commit 29dd0df

File tree

3 files changed

+135
-151
lines changed

3 files changed

+135
-151
lines changed

packages/ketcher-react/src/script/ui/data/convert/structconv.js

+10-17
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@
1414
* limitations under the License.
1515
***************************************************************************/
1616

17-
import { capitalize } from 'lodash/fp'
17+
import { AtomList, Bond, Elements, StereoLabel } from 'ketcher-core'
1818

19-
import { Bond, AtomList, StereoLabel, Elements } from 'ketcher-core'
20-
import { sdataSchema } from '../schema/sdata-schema'
2119
import { atom as atomSchema } from '../schema/struct-schema'
22-
import {
23-
getPredefinedStereoLabels,
24-
predefinedStereoGroups
25-
} from '../../dialog/toolbox/enhancedStereo'
20+
import { capitalize } from 'lodash/fp'
21+
import { sdataSchema } from '../schema/sdata-schema'
22+
23+
const DefaultStereoGroupNumber = 1
2624

2725
export function fromElement(selem) {
2826
if (selem.label === 'R#') {
@@ -126,24 +124,19 @@ export function fromStereoLabel(stereoLabel) {
126124
if (stereoLabel === null) return { type: null }
127125
const type = stereoLabel.match(/\D+/g)[0]
128126
const number = +stereoLabel.replace(type, '')
129-
const defaultOrNumber = predefinedStereoGroups[StereoLabel.Or].max + 1
130-
const defaultAndNumber = predefinedStereoGroups[StereoLabel.And].max + 1
131127

132-
if (
133-
type === StereoLabel.Abs ||
134-
getPredefinedStereoLabels(type).includes(stereoLabel)
135-
) {
128+
if (type === StereoLabel.Abs) {
136129
return {
137130
type: stereoLabel,
138-
orNumber: defaultOrNumber,
139-
andNumber: defaultAndNumber
131+
orNumber: DefaultStereoGroupNumber,
132+
andNumber: DefaultStereoGroupNumber
140133
}
141134
}
142135

143136
if (type === StereoLabel.And) {
144137
return {
145138
type: type,
146-
orNumber: defaultOrNumber,
139+
orNumber: DefaultStereoGroupNumber,
147140
andNumber: number
148141
}
149142
}
@@ -152,7 +145,7 @@ export function fromStereoLabel(stereoLabel) {
152145
return {
153146
type: type,
154147
orNumber: number,
155-
andNumber: defaultOrNumber
148+
andNumber: DefaultStereoGroupNumber
156149
}
157150
}
158151
}
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,10 @@
1-
import React from 'react'
2-
import { connect } from 'react-redux'
3-
4-
import { Dialog } from '../../views/components'
51
import Form, { Field } from '../../component/form/form'
6-
import { StereoLabel } from 'ketcher-core'
7-
8-
export const predefinedStereoGroups = {
9-
[StereoLabel.And]: {
10-
min: 1,
11-
max: 2
12-
},
13-
[StereoLabel.Or]: {
14-
min: 1,
15-
max: 2
16-
}
17-
} as const
18-
19-
export function getPredefinedStereoLabels(stereoLabel: StereoLabel): string[] {
20-
const min = predefinedStereoGroups[stereoLabel].min
21-
const max = predefinedStereoGroups[stereoLabel].max
2+
import { StereoLabel, Struct } from 'ketcher-core'
223

23-
return new Array(max - min + 1)
24-
.fill(null)
25-
.map((_, index) => stereoLabel + (min + index))
26-
}
27-
28-
const enhancedStereoSchema = {
29-
title: 'Enhanced Stereo',
30-
type: 'object',
31-
properties: {
32-
type: {
33-
title: 'Stereo Label',
34-
enum: [
35-
StereoLabel.Abs,
36-
...getPredefinedStereoLabels(StereoLabel.And),
37-
StereoLabel.And,
38-
...getPredefinedStereoLabels(StereoLabel.Or),
39-
StereoLabel.Or
40-
],
41-
enumNames: [
42-
'ABS',
43-
...getPredefinedStereoLabels(StereoLabel.And).map(stereoLabel =>
44-
stereoLabel.replace(StereoLabel.And, 'AND')
45-
),
46-
'AND...',
47-
...getPredefinedStereoLabels(StereoLabel.Or).map(stereoLabel =>
48-
stereoLabel.toUpperCase()
49-
),
50-
'OR...'
51-
]
52-
},
53-
andNumber: {
54-
type: 'integer',
55-
minimum: 1,
56-
invalidMessage: 'Only positive integer'
57-
},
58-
orNumber: {
59-
type: 'integer',
60-
minimum: 1,
61-
invalidMessage: 'Only positive integer'
62-
}
63-
}
64-
}
4+
import { Dialog } from '../../views/components'
5+
import React from 'react'
6+
import { connect } from 'react-redux'
7+
import { range } from 'lodash'
658

669
interface EnhancedStereoResult {
6710
andNumber: number
@@ -79,6 +22,7 @@ interface EnhancedStereoProps {
7922
className: string
8023
init: EnhancedStereoResult & { init?: true }
8124
formState: EnhancedStereoFormState
25+
struct: Struct
8226
}
8327

8428
interface EnhancedStereoCallProps {
@@ -89,24 +33,31 @@ interface EnhancedStereoCallProps {
8933
type Props = EnhancedStereoProps & EnhancedStereoCallProps
9034

9135
const EnhancedStereo: React.FC<Props> = props => {
92-
const { formState, init, ...rest } = props
36+
const { struct, formState, init, ...rest } = props
9337
const { result, valid } = formState
9438

95-
const handleChange = (
96-
stereoLabel: StereoLabel,
97-
newState: EnhancedStereoResult
98-
): EnhancedStereoResult => {
99-
const defaultOrNumber = predefinedStereoGroups[StereoLabel.Or].max + 1
100-
const defaultAndNumber = predefinedStereoGroups[StereoLabel.And].max + 1
39+
const stereoLabels: Array<string> = findStereLabels(
40+
struct,
41+
Array.from(struct.atoms.keys())
42+
)
10143

102-
if (andNumberDisabled(stereoLabel)) {
103-
newState.andNumber = defaultAndNumber
104-
}
105-
if (orNumberDisabled(stereoLabel)) {
106-
newState.orNumber = defaultOrNumber
44+
const maxAnd: number = maxOfAnds(stereoLabels)
45+
const maxOr: number = maxOfOrs(stereoLabels)
46+
47+
const enhancedStereoSchema = {
48+
title: 'Enhanced Stereo',
49+
type: 'object',
50+
properties: {
51+
type: {
52+
type: 'string'
53+
},
54+
andNumber: {
55+
type: 'integer'
56+
},
57+
orNumber: {
58+
type: 'integer'
59+
}
10760
}
108-
109-
return newState
11061
}
11162

11263
return (
@@ -116,74 +67,104 @@ const EnhancedStereo: React.FC<Props> = props => {
11667
params={rest}
11768
result={() => result}
11869
valid={() => valid}
119-
buttons={['OK', 'Close']}>
70+
buttons={['Cancel', 'OK']}>
12071
<Form schema={enhancedStereoSchema} init={init} {...formState}>
121-
<Field
122-
name="type"
123-
component={FieldSet}
124-
labelPos={false}
125-
onChange={handleChange}
126-
/>
127-
</Form>
128-
</Dialog>
129-
)
130-
}
131-
132-
interface FieldSetProps {
133-
name: string
134-
schema: any
135-
value: StereoLabel
136-
onChange: (value: string) => void
137-
type?: string
138-
}
139-
140-
const FieldSet: React.FC<FieldSetProps> = props => {
141-
const { schema, value, onChange, type = 'radio', ...rest } = props
142-
143-
return (
144-
<fieldset>
145-
{schema.enum.map((val, index) => (
146-
<li key={schema.enumNames[index]} className="stereo-label-item">
147-
<label className="stereo-label-name">
148-
<input
149-
type={type}
150-
checked={val === value}
151-
value={val}
152-
onChange={() => onChange(val)}
153-
{...rest}
72+
<fieldset>
73+
<label className="stereo-label-item">
74+
<Field
75+
name="type"
76+
labelPos={false}
77+
type="radio"
78+
value={StereoLabel.Abs}
79+
checked={result.type === StereoLabel.Abs}
15480
/>
155-
{schema.enumNames[index]}
81+
ABS
15682
</label>
157-
{val === StereoLabel.And && (
83+
{maxAnd !== 0 && (
84+
<label className="stereo-label-item">
85+
<Field
86+
name="type"
87+
labelPos={false}
88+
type="radio"
89+
value={StereoLabel.And}
90+
checked={result.type === StereoLabel.And}
91+
/>
92+
Add to AND
93+
<Field
94+
name="andNumber"
95+
schema={range(1, maxAnd + 1)}
96+
type="text"
97+
className="label-group-select"
98+
/>
99+
Group
100+
</label>
101+
)}
102+
{maxOr !== 0 && (
103+
<label className="stereo-label-item">
104+
<Field
105+
name="type"
106+
labelPos={false}
107+
type="radio"
108+
value={StereoLabel.Or}
109+
checked={result.type === StereoLabel.Or}
110+
/>
111+
Add to OR
112+
<Field
113+
name="orNumber"
114+
schema={range(1, maxOr + 1)}
115+
type="text"
116+
className="label-group-select"
117+
/>
118+
Group
119+
</label>
120+
)}
121+
<label className="stereo-label-item">
158122
<Field
159-
name="andNumber"
160-
type="text"
161-
className="label-group-value"
162-
disabled={andNumberDisabled(value)}
123+
name="type"
124+
labelPos={false}
125+
type="radio"
126+
value={`&${maxOr + 1}`}
163127
/>
164-
)}
165-
{val === StereoLabel.Or && (
128+
Create new AND Group
129+
</label>
130+
<label className="stereo-label-item">
166131
<Field
167-
name="orNumber"
168-
type="text"
169-
className="label-group-value"
170-
disabled={orNumberDisabled(value)}
132+
name="type"
133+
labelPos={false}
134+
type="radio"
135+
value={`or${maxOr + 1}`}
171136
/>
172-
)}
173-
</li>
174-
))}
175-
</fieldset>
137+
Create new OR Group
138+
</label>
139+
</fieldset>
140+
</Form>
141+
</Dialog>
142+
)
143+
}
144+
145+
// TODO: Move the function below to Struct class
146+
function findStereLabels(struct, aids): Array<string> {
147+
const stereoIds = aids.filter(
148+
aid => struct.atoms.get(aid).stereoLabel !== null
176149
)
150+
return stereoIds.map(aid => struct.atoms.get(aid).stereoLabel)
177151
}
178152

179-
function andNumberDisabled(stereoLabel: StereoLabel): boolean {
180-
return !stereoLabel || stereoLabel !== StereoLabel.And || stereoLabel === null
153+
function maxOfAnds(stereLabels): number {
154+
const numbers = stereLabels.map(label => {
155+
return label.match(/&/) ? +label.match(/\d+/)?.join() : 0
156+
})
157+
return Math.max(...numbers)
181158
}
182159

183-
function orNumberDisabled(stereoLabel: StereoLabel): boolean {
184-
return !stereoLabel || stereoLabel !== StereoLabel.Or || stereoLabel === null
160+
function maxOfOrs(stereLabels): number {
161+
const numbers = stereLabels.map(label => {
162+
return label.match(/or/) ? +label.match(/\d+/)?.join() : 0
163+
})
164+
return Math.max(...numbers)
185165
}
186166

187-
export default connect(store => ({
188-
formState: store.modal.form || { result: {}, valid: false }
167+
export default connect(state => ({
168+
formState: state.modal.form || { result: {}, valid: false },
169+
struct: state.editor.struct()
189170
}))(EnhancedStereo)

packages/ketcher-react/src/style/dialogs.less

+12-2
Original file line numberDiff line numberDiff line change
@@ -175,21 +175,31 @@
175175
}
176176

177177
.enhanced-stereo {
178+
width: 330px;
179+
178180
.stereo-label-item {
179181
display: flex;
180182
align-items: center;
181183
min-height: 2.4em;
182184
margin: 2px;
183185
}
186+
184187
.stereo-label-name {
185188
display: flex;
186189
align-items: center;
187190
min-width: 60px;
188-
margin-right: 15px;
191+
margin-right: 5px;
189192
}
193+
190194
.label-group-value {
191-
width: 48px;
195+
width: 22px;
192196
margin: 0;
197+
margin-right: 5px;
198+
}
199+
200+
.label-group-select {
201+
margin-right: 5px;
202+
margin-left: 5px;
193203
}
194204
}
195205

0 commit comments

Comments
 (0)