From a1feea22801850a64b1b238b4bfcdeedd8220125 Mon Sep 17 00:00:00 2001 From: Nikita Chistousov Date: Wed, 28 Aug 2024 15:43:07 +0200 Subject: [PATCH 1/4] =?UTF-8?q?#4555=20=E2=80=93=20Add=20preview=20for=20a?= =?UTF-8?q?mbiguous=20monomers=20in=20the=20library=20and=20on=20canvas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/EditorEvents.tsx | 29 ++++- .../monomerLibraryGroup/MonomerGroup.tsx | 32 ++++-- .../src/components/preview/Preview.tsx | 3 + .../AmbiguousMonomerPreview.styles.ts | 54 +++++++++ .../AmbiguousMonomerPreview.tsx | 104 ++++++++++++++++++ .../src/helpers/calculatePreviewTop.ts | 18 ++- .../src/state/common/editorSlice.ts | 11 +- 7 files changed, 239 insertions(+), 12 deletions(-) create mode 100644 packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts create mode 100644 packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx diff --git a/packages/ketcher-macromolecules/src/EditorEvents.tsx b/packages/ketcher-macromolecules/src/EditorEvents.tsx index 5885cc01e7..75a78c676a 100644 --- a/packages/ketcher-macromolecules/src/EditorEvents.tsx +++ b/packages/ketcher-macromolecules/src/EditorEvents.tsx @@ -15,6 +15,7 @@ ***************************************************************************/ import { useCallback, useEffect } from 'react'; import { + AmbiguousMonomerPreviewState, BondPreviewState, MonomerPreviewState, PresetPosition, @@ -33,8 +34,14 @@ import { } from 'components/modal/modalContainer'; import { useAppDispatch, useAppSelector } from 'hooks'; import { debounce } from 'lodash'; -import { Nucleoside, Nucleotide } from 'ketcher-core'; import { + AmbiguousMonomer, + BaseMonomer, + Nucleoside, + Nucleotide, +} from 'ketcher-core'; +import { + calculateAmbiguousMonomerPreviewTop, calculateBondPreviewPosition, calculateMonomerPreviewTop, calculateNucleoElementPreviewTop, @@ -173,7 +180,25 @@ export const EditorEvents = () => { const cardCoordinates = e.target.getBoundingClientRect(); const left = `${cardCoordinates.left + cardCoordinates.width / 2}px`; const sequenceNode = e.target.__data__?.node; - const monomer = e.target.__data__?.monomer || sequenceNode?.monomer; + const monomer: BaseMonomer | AmbiguousMonomer = + e.target.__data__?.monomer || sequenceNode?.monomer; + + if (monomer instanceof AmbiguousMonomer) { + const ambiguousMonomerPreviewData: AmbiguousMonomerPreviewState = { + type: PreviewType.AmbiguousMonomer, + monomer: monomer.variantMonomerItem, + style: { + left, + top: calculateAmbiguousMonomerPreviewTop( + monomer.variantMonomerItem, + )(cardCoordinates), + }, + }; + + debouncedShowPreview(ambiguousMonomerPreviewData); + return; + } + const monomerItem = monomer.monomerItem; const attachmentPointsToBonds = { ...monomer.attachmentPointsToBonds }; const isNucleotideOrNucleoside = diff --git a/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx b/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx index 35e04cabe9..71bb2a01c1 100644 --- a/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx +++ b/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx @@ -14,7 +14,11 @@ * limitations under the License. ***************************************************************************/ import { useCallback } from 'react'; -import { calculateMonomerPreviewTop, EmptyFunction } from 'helpers'; +import { + calculateAmbiguousMonomerPreviewTop, + calculateMonomerPreviewTop, + EmptyFunction, +} from 'helpers'; import { debounce } from 'lodash'; import { MonomerItem } from '../monomerLibraryItem'; import { GroupContainerColumn, GroupTitle, ItemsContainer } from './styles'; @@ -26,7 +30,7 @@ import { } from 'ketcher-core'; import { useAppDispatch, useAppSelector } from 'hooks'; import { - MonomerPreviewState, + PreviewStyle, PreviewType, selectEditor, selectTool, @@ -89,18 +93,30 @@ const MonomerGroup = ({ e: React.MouseEvent, ) => { handleItemMouseLeave(); + const cardCoordinates = e.currentTarget.getBoundingClientRect(); + let style: PreviewStyle; + let previewType: PreviewType; + let top: string; + if (isAmbiguousMonomerLibraryItem(monomer)) { - return; + top = monomer + ? calculateAmbiguousMonomerPreviewTop(monomer)(cardCoordinates) + : ''; + const left = `${cardCoordinates.left + cardCoordinates.width / 2}px`; + previewType = PreviewType.AmbiguousMonomer; + style = { left, top }; + } else { + top = monomer ? calculateMonomerPreviewTop(cardCoordinates) : ''; + style = { right: '-88px', top }; + previewType = PreviewType.Monomer; } - const cardCoordinates = e.currentTarget.getBoundingClientRect(); - const top = monomer ? calculateMonomerPreviewTop(cardCoordinates) : ''; - const style = { right: '-88px', top }; - const previewData: MonomerPreviewState = { - type: PreviewType.Monomer, + const previewData = { + type: previewType, monomer, style, }; + debouncedShowPreview(previewData); }; diff --git a/packages/ketcher-macromolecules/src/components/preview/Preview.tsx b/packages/ketcher-macromolecules/src/components/preview/Preview.tsx index 9b278c9b1d..51fe4cc1fc 100644 --- a/packages/ketcher-macromolecules/src/components/preview/Preview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/Preview.tsx @@ -18,6 +18,7 @@ import { PreviewType, selectShowPreview } from 'state/common'; import MonomerPreview from './components/MonomerPreview/MonomerPreview'; import PresetPreview from './components/PresetPreview/PresetPreview'; import BondPreview from './components/BondPreview/BondPreview'; +import AmbiguousMonomerPreview from './components/AmbiguousMonomerPreview/AmbiguousMonomerPreview'; export const Preview = () => { const preview = useAppSelector(selectShowPreview); @@ -33,6 +34,8 @@ export const Preview = () => { return ; case PreviewType.Bond: return ; + case PreviewType.AmbiguousMonomer: + return ; default: return null; } diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts new file mode 100644 index 0000000000..b52c8d83c8 --- /dev/null +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts @@ -0,0 +1,54 @@ +import styled from '@emotion/styled'; + +export const Container = styled.div` + display: flex; + flex-direction: column; + align-items: start; + gap: 16px; + padding: 8px; + background: ${(props) => props.theme.ketcher.color.background.primary}; + border: ${(props) => props.theme.ketcher.border.regular}; + border-radius: ${(props) => props.theme.ketcher.border.radius.regular}; + box-shadow: ${(props) => props.theme.ketcher.shadow.regular}; +`; + +export const Header = styled.div` + font-weight: 600; +`; + +export const Content = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +export const ContentLine = styled.div` + height: 24px; + display: flex; + align-items: center; + gap: 8px; +`; + +interface RatioBarProps { + ratio: number; +} + +export const RatioBar = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 36px; + border-right: 0.5px solid #b4b9d6; + background: ${({ ratio }) => `linear-gradient( + 90deg, + #ffffff 0%, + #ffffff ${100 - ratio}%, + #e7f7ea ${100 - ratio + 0.5}%, + #e7f7ea 100% + )`}; +`; + +export const MonomerName = styled.div` + flex: 1; +`; diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx new file mode 100644 index 0000000000..953418008e --- /dev/null +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx @@ -0,0 +1,104 @@ +import { useAppSelector } from 'hooks'; +import { AmbiguousMonomerPreviewState, selectShowPreview } from 'state/common'; +import { useMemo } from 'react'; +import styled from '@emotion/styled'; +import { + Container, + Content, + ContentLine, + Header, + RatioBar, +} from './AmbiguousMonomerPreview.styles'; + +interface Props { + className?: string; +} + +const AmbiguousMonomerPreview = ({ className }: Props) => { + const preview = useAppSelector( + selectShowPreview, + ) as AmbiguousMonomerPreviewState; + + const { monomer, style } = preview; + + const isAlternatives = monomer.subtype === 'alternatives'; + + const ContainerDynamic = useMemo(() => { + if (!style) { + return styled(Container)``; + } + + return styled(Container)` + top: ${style?.top || ''}; + left: ${style?.left || ''}; + right: ${style?.right || ''}; + `; + }, [style]); + + const header = isAlternatives ? 'Alternatives' : 'Mixed'; + + const aminoAcidFallback = monomer.label === 'X' ? 'Any amino acid' : null; + const baseFallback = monomer.label === 'N' ? 'Any base' : null; + const fallback = aminoAcidFallback || baseFallback; + + const { monomers, options } = monomer; + + const previewData: { monomerName: string; ratio?: number }[] = useMemo(() => { + if (fallback) { + return []; + } + + return monomers.map((monomer) => { + const option = options.find( + (option) => option.templateId === monomer.monomerItem.props.id, + ); + return { + monomerName: monomer.monomerItem.props.Name, + ratio: option?.ratio, + }; + }); + }, [fallback, monomers, options]); + + const preparedPreviewData = useMemo(() => { + const sortedData = previewData.sort((a, b) => { + if (isAlternatives) { + return a.monomerName.localeCompare(b.monomerName); + } else { + if (!a.ratio || !b.ratio) { + return 0; + } + return b.ratio - a.ratio; + } + }); + + return sortedData.slice(0, 5); + }, [previewData, isAlternatives]); + + return ( + +
{header}
+ + {fallback ?? + preparedPreviewData.map((entry) => ( + + {entry.ratio && ( + {entry.ratio}% + )} + {entry.monomerName} + + ))} + +
+ ); +}; + +const StyledPreview = styled(AmbiguousMonomerPreview)` + z-index: 5; + position: absolute; + transform: translate(-50%, 0); +`; + +export default StyledPreview; diff --git a/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts b/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts index b68be082ae..8ca564b4c7 100644 --- a/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts +++ b/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ -import { PolymerBond } from 'ketcher-core'; +import { AmbiguousMonomerType, PolymerBond } from 'ketcher-core'; import { preview } from '../constants'; import { PreviewStyle } from 'state/common'; import assert from 'assert'; @@ -43,6 +43,22 @@ function createCalculatePreviewTopFunction( }; } +const calculateAmbiguousPreviewHeight = (monomersCount: number) => { + const headingHeight = 16; + const monomersHeight = 24 * monomersCount; + return headingHeight + monomersHeight; +}; + +export const calculateAmbiguousMonomerPreviewTop = ( + monomer: AmbiguousMonomerType, +) => { + const shouldHaveOneLine = monomer.label === 'X' || monomer.label === 'N'; + const monomersCount = shouldHaveOneLine ? 1 : monomer.monomers.length; + const monomersCountToUse = Math.max(5, monomersCount); + const height = calculateAmbiguousPreviewHeight(monomersCountToUse); + return createCalculatePreviewTopFunction(height); +}; + export const calculateBondPreviewPosition = ( bond: PolymerBond, bondCoordinates: DOMRect, diff --git a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts index 9f25e0e807..488ba7f5f0 100644 --- a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts +++ b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts @@ -21,6 +21,7 @@ import { MonomerItemType, PolymerBond, AttachmentPointsToBonds, + AmbiguousMonomerType, } from 'ketcher-core'; import { RootState } from 'state'; import { ThemeType } from 'theming/defaultTheme'; @@ -30,6 +31,7 @@ export enum PreviewType { Monomer = 'monomer', Preset = 'preset', Bond = 'bond', + AmbiguousMonomer = 'ambiguousMonomer', } export interface PreviewStyle { @@ -70,10 +72,17 @@ export interface BondPreviewState extends BasePreviewState { readonly polymerBond: PolymerBond; } +export interface AmbiguousMonomerPreviewState extends BasePreviewState { + readonly type: PreviewType.AmbiguousMonomer; + readonly monomer: AmbiguousMonomerType; + readonly preset?: boolean; +} + type EditorStatePreview = | MonomerPreviewState | PresetPreviewState - | BondPreviewState; + | BondPreviewState + | AmbiguousMonomerPreviewState; // TODO: Looks like we do not use `isReady`. Delete? interface EditorState { From ac0348e945911d5341a3b3f4674c7e7fe6ce8c16 Mon Sep 17 00:00:00 2001 From: Nikita Chistousov Date: Thu, 29 Aug 2024 10:56:47 +0200 Subject: [PATCH 2/4] =?UTF-8?q?#4555=20=E2=80=93=20Add=20preview=20for=20a?= =?UTF-8?q?mbiguous=20monomers=20in=20sequence=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ketcher-macromolecules/src/EditorEvents.tsx | 17 +++++++++++++++++ .../AmbiguousMonomerPreview.tsx | 17 ++++++++++++++--- .../src/state/common/editorSlice.ts | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/ketcher-macromolecules/src/EditorEvents.tsx b/packages/ketcher-macromolecules/src/EditorEvents.tsx index 75a78c676a..acd8357395 100644 --- a/packages/ketcher-macromolecules/src/EditorEvents.tsx +++ b/packages/ketcher-macromolecules/src/EditorEvents.tsx @@ -218,6 +218,23 @@ export const EditorEvents = () => { sequenceNode.rnaBase.monomerItem, ]; + if (sequenceNode.rnaBase instanceof AmbiguousMonomer) { + const ambiguousMonomerPreviewData: AmbiguousMonomerPreviewState = { + type: PreviewType.AmbiguousMonomer, + monomer: sequenceNode.rnaBase.variantMonomerItem, + presetMonomers: monomers, + style: { + left, + top: calculateAmbiguousMonomerPreviewTop( + sequenceNode.rnaBase.variantMonomerItem, + )(cardCoordinates), + }, + }; + + debouncedShowPreview(ambiguousMonomerPreviewData); + return; + } + const existingPreset = presets.find((preset) => { const presetMonomers = [preset.sugar, preset.base, preset.phosphate]; return monomers.every((monomer, index) => { diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx index 953418008e..b1e40e26d2 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx @@ -19,7 +19,7 @@ const AmbiguousMonomerPreview = ({ className }: Props) => { selectShowPreview, ) as AmbiguousMonomerPreviewState; - const { monomer, style } = preview; + const { monomer, presetMonomers, style } = preview; const isAlternatives = monomer.subtype === 'alternatives'; @@ -52,12 +52,23 @@ const AmbiguousMonomerPreview = ({ className }: Props) => { const option = options.find( (option) => option.templateId === monomer.monomerItem.props.id, ); + + let monomerName: string; + if (presetMonomers) { + const [sugar, , phosphate] = presetMonomers; + const sugarName = sugar?.label ?? ''; + const phosphateName = phosphate?.label ?? ''; + monomerName = `${sugarName}(${monomer.label})${phosphateName}`; + } else { + monomerName = monomer.monomerItem.props.Name; + } + return { - monomerName: monomer.monomerItem.props.Name, + monomerName, ratio: option?.ratio, }; }); - }, [fallback, monomers, options]); + }, [fallback, monomers, presetMonomers, options]); const preparedPreviewData = useMemo(() => { const sortedData = previewData.sort((a, b) => { diff --git a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts index 488ba7f5f0..d3a68f421e 100644 --- a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts +++ b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts @@ -75,7 +75,7 @@ export interface BondPreviewState extends BasePreviewState { export interface AmbiguousMonomerPreviewState extends BasePreviewState { readonly type: PreviewType.AmbiguousMonomer; readonly monomer: AmbiguousMonomerType; - readonly preset?: boolean; + readonly presetMonomers?: ReadonlyArray; } type EditorStatePreview = From 09c77ca41e10373e07aac4674b77c812af3c7e3a Mon Sep 17 00:00:00 2001 From: Roman Rodionov Date: Sun, 1 Sep 2024 16:28:12 +0200 Subject: [PATCH 3/4] - fixed preview position - fixed preview of ambiguous monomer in molecules mode --- .../editor/MacromoleculesConverter.ts | 39 ++++++++++----- .../sequence/RNASequenceItemRenderer.ts | 6 +-- .../src/application/render/restruct/rebond.ts | 4 +- .../src/domain/entities/sgroup.ts | 10 ++-- .../src/domain/helpers/monomers.ts | 4 +- .../src/EditorEvents.tsx | 12 +++-- .../RnaPresetGroup/RnaPresetGroup.tsx | 39 ++++++++++----- .../monomerLibraryGroup/MonomerGroup.tsx | 9 +--- .../src/components/preview/Preview.tsx | 5 +- .../AmbiguousMonomerPreview.styles.ts | 9 ++-- .../AmbiguousMonomerPreview.tsx | 12 ++--- .../PresetPreview/PresetPreview.tsx | 2 +- ...viewTop.ts => calculatePreviewPosition.ts} | 47 +++++++++++++++---- .../src/helpers/index.ts | 2 +- .../src/state/common/editorSlice.ts | 21 ++------- .../ketcher-macromolecules/src/state/index.ts | 1 + .../ketcher-macromolecules/src/state/types.ts | 13 +++++ .../components/StructEditor/InfoPanel.tsx | 30 +++++++++++- 18 files changed, 176 insertions(+), 89 deletions(-) rename packages/ketcher-macromolecules/src/helpers/{calculatePreviewTop.ts => calculatePreviewPosition.ts} (69%) create mode 100644 packages/ketcher-macromolecules/src/state/types.ts diff --git a/packages/ketcher-core/src/application/editor/MacromoleculesConverter.ts b/packages/ketcher-core/src/application/editor/MacromoleculesConverter.ts index 34a16cf825..e696fd8584 100644 --- a/packages/ketcher-core/src/application/editor/MacromoleculesConverter.ts +++ b/packages/ketcher-core/src/application/editor/MacromoleculesConverter.ts @@ -1,4 +1,5 @@ import { + AmbiguousMonomer, Atom, Bond, FunctionalGroup, @@ -88,14 +89,15 @@ export class MacromoleculesConverter { const attachmentPoint = monomer.monomerItem.attachmentPoints?.[attachmentPointIndex]; const atomIdMap = monomerToAtomIdMap.get(monomer); + const attachmentPointAtomId = + monomer instanceof AmbiguousMonomer ? 0 : attachmentPoint?.attachmentAtom; return { globalAttachmentAtomId: - isNumber(attachmentPoint?.attachmentAtom) && - atomIdMap?.get(attachmentPoint?.attachmentAtom as number), + isNumber(attachmentPointAtomId) && + atomIdMap?.get(attachmentPointAtomId as number), attachmentAtomId: - isNumber(attachmentPoint?.attachmentAtom) && - attachmentPoint?.attachmentAtom, + isNumber(attachmentPointAtomId) && attachmentPointAtomId, attachmentPointNumber, }; } @@ -132,8 +134,16 @@ export class MacromoleculesConverter { monomerMicromolecule.id, new ReSGroup(monomerMicromolecule), ); - - monomer.monomerItem.struct.atoms.forEach((oldAtom, oldAtomId) => { + const monomerAtoms = + monomer instanceof AmbiguousMonomer + ? monomer.monomers[0].monomerItem.struct.atoms + : monomer.monomerItem.struct.atoms; + const monomerBonds = + monomer instanceof AmbiguousMonomer + ? monomer.monomers[0].monomerItem.struct.bonds + : monomer.monomerItem.struct.bonds; + + monomerAtoms.forEach((oldAtom, oldAtomId) => { const { atom, atomId } = this.addMonomerAtomToStruct( oldAtom, monomer, @@ -162,9 +172,10 @@ export class MacromoleculesConverter { ); }, ) || [], + false, ); struct.sGroupForest.insert(monomerMicromolecule); - monomer.monomerItem.struct.bonds.forEach((bond) => { + monomerBonds.forEach((bond) => { const bondClone = bond.clone(); bondClone.begin = atomIdsMap.get(bondClone.begin) as number; bondClone.end = atomIdsMap.get(bondClone.end) as number; @@ -224,10 +235,16 @@ export class MacromoleculesConverter { sgroupToMonomer: Map, ) { const command = new Command(); - const monomerAdditionCommand = drawingEntitiesManager.addMonomer( - monomerMicromolecule.monomer.monomerItem, - monomerMicromolecule.pp as Vec2, - ); + const monomerAdditionCommand = + monomerMicromolecule.monomer instanceof AmbiguousMonomer + ? drawingEntitiesManager.addAmbiguousMonomer( + monomerMicromolecule.monomer.variantMonomerItem, + monomerMicromolecule.monomer.position, + ) + : drawingEntitiesManager.addMonomer( + monomerMicromolecule.monomer.monomerItem, + monomerMicromolecule.pp as Vec2, + ); command.merge(monomerAdditionCommand); sgroupToMonomer.set( monomerMicromolecule, diff --git a/packages/ketcher-core/src/application/render/renderers/sequence/RNASequenceItemRenderer.ts b/packages/ketcher-core/src/application/render/renderers/sequence/RNASequenceItemRenderer.ts index 41d6c19ec7..3315d4f29c 100644 --- a/packages/ketcher-core/src/application/render/renderers/sequence/RNASequenceItemRenderer.ts +++ b/packages/ketcher-core/src/application/render/renderers/sequence/RNASequenceItemRenderer.ts @@ -32,10 +32,8 @@ export abstract class RNASequenceItemRenderer extends BaseSequenceItemRenderer { get symbolToDisplay(): string { return this.node.rnaBase instanceof AmbiguousMonomer - ? this.node.monomer.label - : this.node.monomer.attachmentPointsToBonds.R3?.getAnotherMonomer( - this.node.monomer, - )?.monomerItem?.props.MonomerNaturalAnalogCode || '@'; + ? this.node.rnaBase.label + : this.node.rnaBase.monomerItem?.props.MonomerNaturalAnalogCode || '@'; } protected drawCommonModification(node: Nucleoside | Nucleotide) { diff --git a/packages/ketcher-core/src/application/render/restruct/rebond.ts b/packages/ketcher-core/src/application/render/restruct/rebond.ts index 1519bd3c40..4dfa99fbe8 100644 --- a/packages/ketcher-core/src/application/render/restruct/rebond.ts +++ b/packages/ketcher-core/src/application/render/restruct/rebond.ts @@ -15,6 +15,7 @@ ***************************************************************************/ import { + AmbiguousMonomer, Atom, Bond, FunctionalGroup, @@ -64,7 +65,8 @@ class ReBond extends ReObject { atomId: number, sgroup?: SGroup, ) { - return sgroup instanceof MonomerMicromolecule + return sgroup instanceof MonomerMicromolecule && + !(sgroup.monomer instanceof AmbiguousMonomer) ? (sgroup.getAttachmentAtomId() as number) : sgroup?.isContracted() ? sgroup?.getContractedPosition(struct).atomId diff --git a/packages/ketcher-core/src/domain/entities/sgroup.ts b/packages/ketcher-core/src/domain/entities/sgroup.ts index c10124eed2..b04ff05186 100644 --- a/packages/ketcher-core/src/domain/entities/sgroup.ts +++ b/packages/ketcher-core/src/domain/entities/sgroup.ts @@ -243,14 +243,17 @@ export class SGroup { return this.getConnectionPointsCount(struct) >= 1; } - addAttachmentPoint(attachmentPoint: SGroupAttachmentPoint): void { + addAttachmentPoint( + attachmentPoint: SGroupAttachmentPoint, + validateUniqueness = true, + ): void { const isAttachmentPointAlreadyExist = this.attachmentPoints.some( ({ atomId, leaveAtomId }) => attachmentPoint.atomId === atomId && attachmentPoint.leaveAtomId === leaveAtomId, ); - if (isAttachmentPointAlreadyExist) { + if (isAttachmentPointAlreadyExist && validateUniqueness) { throw new Error( 'The same attachment point cannot be added to an S-group more than once', ); @@ -263,9 +266,10 @@ export class SGroup { attachmentPoints: | ReadonlyArray | SGroupAttachmentPoint[], + validateUniqueness = true, ): void { for (const attachmentPoint of attachmentPoints) { - this.addAttachmentPoint(attachmentPoint); + this.addAttachmentPoint(attachmentPoint, validateUniqueness); } } diff --git a/packages/ketcher-core/src/domain/helpers/monomers.ts b/packages/ketcher-core/src/domain/helpers/monomers.ts index ca40b92c86..bb8a4591e9 100644 --- a/packages/ketcher-core/src/domain/helpers/monomers.ts +++ b/packages/ketcher-core/src/domain/helpers/monomers.ts @@ -207,9 +207,9 @@ export const isRnaBaseVariantMonomer = ( ) => monomer.monomerClass === KetMonomerClass.Base; export function isAmbiguousMonomerLibraryItem( - monomer: MonomerOrAmbiguousType, + monomer?: MonomerOrAmbiguousType, ): monomer is AmbiguousMonomerType { - return Boolean(monomer.isAmbiguous); + return Boolean(monomer && monomer.isAmbiguous); } export function isPeptideOrAmbiguousPeptide( diff --git a/packages/ketcher-macromolecules/src/EditorEvents.tsx b/packages/ketcher-macromolecules/src/EditorEvents.tsx index acd8357395..3d7315d3b7 100644 --- a/packages/ketcher-macromolecules/src/EditorEvents.tsx +++ b/packages/ketcher-macromolecules/src/EditorEvents.tsx @@ -20,8 +20,6 @@ import { MonomerPreviewState, PresetPosition, PresetPreviewState, - PreviewStyle, - PreviewType, selectEditor, selectEditorActiveTool, selectTool, @@ -41,6 +39,7 @@ import { Nucleotide, } from 'ketcher-core'; import { + calculateAmbiguousMonomerPreviewLeft, calculateAmbiguousMonomerPreviewTop, calculateBondPreviewPosition, calculateMonomerPreviewTop, @@ -48,6 +47,7 @@ import { } from 'helpers'; import { selectAllPresets } from 'state/rna-builder'; import { PolymerBond } from 'ketcher-core/dist/domain/entities/PolymerBond'; +import { PreviewStyle, PreviewType } from 'state/types'; const noPreviewTools = ['bond-single']; @@ -188,7 +188,9 @@ export const EditorEvents = () => { type: PreviewType.AmbiguousMonomer, monomer: monomer.variantMonomerItem, style: { - left, + left: `${calculateAmbiguousMonomerPreviewLeft( + cardCoordinates.left, + )}px`, top: calculateAmbiguousMonomerPreviewTop( monomer.variantMonomerItem, )(cardCoordinates), @@ -224,7 +226,9 @@ export const EditorEvents = () => { monomer: sequenceNode.rnaBase.variantMonomerItem, presetMonomers: monomers, style: { - left, + left: `${calculateAmbiguousMonomerPreviewLeft( + cardCoordinates.left, + )}px`, top: calculateAmbiguousMonomerPreviewTop( sequenceNode.rnaBase.variantMonomerItem, )(cardCoordinates), diff --git a/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx b/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx index 8f64b900f1..f03864825f 100644 --- a/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx +++ b/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx @@ -14,9 +14,12 @@ * limitations under the License. ***************************************************************************/ -import { calculateNucleoElementPreviewTop } from 'helpers'; +import { + calculateAmbiguousMonomerPreviewTop, + calculateNucleoElementPreviewTop, +} from 'helpers'; import { useAppSelector } from 'hooks'; -import { MonomerItemType } from 'ketcher-core'; +import { MonomerItemType, isAmbiguousMonomerLibraryItem } from 'ketcher-core'; import { debounce } from 'lodash'; import React, { ReactElement, useCallback } from 'react'; import { @@ -33,9 +36,9 @@ import { ItemsContainer, } from 'components/monomerLibrary/monomerLibraryGroup/styles'; import { + AmbiguousMonomerPreviewState, PresetPosition, PresetPreviewState, - PreviewType, selectEditor, selectShowPreview, showPreview, @@ -44,6 +47,7 @@ import { RNAContextMenu } from 'components/contextMenu/RNAContextMenu'; import { CONTEXT_MENU_ID } from 'components/contextMenu/types'; import { useContextMenu } from 'react-contexify'; import { IRnaPreset } from '../RnaBuilder/types'; +import { PreviewType } from 'state'; export const RnaPresetGroup = ({ presets, duplicatePreset, editPreset }) => { const activePreset = useAppSelector(selectActivePreset); @@ -143,18 +147,27 @@ export const RnaPresetGroup = ({ presets, duplicatePreset, editPreset }) => { const cardCoordinates = e.currentTarget.getBoundingClientRect(); const style = { left: `${cardCoordinates.left + cardCoordinates.width}px`, - top: preset ? calculateNucleoElementPreviewTop(cardCoordinates) : '', + top: isAmbiguousMonomerLibraryItem(preset.base) + ? calculateAmbiguousMonomerPreviewTop(preset.base)(cardCoordinates) + : calculateNucleoElementPreviewTop(cardCoordinates), transform: 'translate(-100%, 0)', }; - - const previewData: PresetPreviewState = { - type: PreviewType.Preset, - monomers, - name: preset.name, - idtAliases: preset.idtAliases, - position: PresetPosition.Library, - style, - }; + const previewData: PresetPreviewState | AmbiguousMonomerPreviewState = + isAmbiguousMonomerLibraryItem(preset.base) + ? { + type: PreviewType.AmbiguousMonomer, + monomer: preset.base, + presetMonomers: monomers, + style, + } + : { + type: PreviewType.Preset, + monomers, + name: preset.name, + idtAliases: preset.idtAliases, + position: PresetPosition.Library, + style, + }; debouncedShowPreview(previewData); }; // endregion # Preview diff --git a/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx b/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx index 71bb2a01c1..83ba361b5d 100644 --- a/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx +++ b/packages/ketcher-macromolecules/src/components/monomerLibrary/monomerLibraryGroup/MonomerGroup.tsx @@ -29,14 +29,9 @@ import { MonomerOrAmbiguousType, } from 'ketcher-core'; import { useAppDispatch, useAppSelector } from 'hooks'; -import { - PreviewStyle, - PreviewType, - selectEditor, - selectTool, - showPreview, -} from 'state/common'; +import { selectEditor, selectTool, showPreview } from 'state/common'; import { selectGroupItemValidations } from 'state/rna-builder'; +import { PreviewStyle, PreviewType } from 'state'; const MonomerGroup = ({ items, diff --git a/packages/ketcher-macromolecules/src/components/preview/Preview.tsx b/packages/ketcher-macromolecules/src/components/preview/Preview.tsx index 51fe4cc1fc..4c0193bfb1 100644 --- a/packages/ketcher-macromolecules/src/components/preview/Preview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/Preview.tsx @@ -14,11 +14,12 @@ * limitations under the License. ***************************************************************************/ import { useAppSelector } from 'hooks'; -import { PreviewType, selectShowPreview } from 'state/common'; +import { selectShowPreview } from 'state/common'; import MonomerPreview from './components/MonomerPreview/MonomerPreview'; import PresetPreview from './components/PresetPreview/PresetPreview'; import BondPreview from './components/BondPreview/BondPreview'; import AmbiguousMonomerPreview from './components/AmbiguousMonomerPreview/AmbiguousMonomerPreview'; +import { PreviewType } from 'state'; export const Preview = () => { const preview = useAppSelector(selectShowPreview); @@ -35,7 +36,7 @@ export const Preview = () => { case PreviewType.Bond: return ; case PreviewType.AmbiguousMonomer: - return ; + return ; default: return null; } diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts index b52c8d83c8..fd7a07f62c 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.styles.ts @@ -6,10 +6,10 @@ export const Container = styled.div` align-items: start; gap: 16px; padding: 8px; - background: ${(props) => props.theme.ketcher.color.background.primary}; - border: ${(props) => props.theme.ketcher.border.regular}; - border-radius: ${(props) => props.theme.ketcher.border.radius.regular}; - box-shadow: ${(props) => props.theme.ketcher.shadow.regular}; + background: #ffffff; + border: 1px solid #cad3dd; + border-radius: 4px; + box-shadow: 0px 1px 1px rgba(197, 203, 207, 0.7); `; export const Header = styled.div` @@ -27,6 +27,7 @@ export const ContentLine = styled.div` display: flex; align-items: center; gap: 8px; + white-space: nowrap; `; interface RatioBarProps { diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx index b1e40e26d2..2b8d6ddbc2 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx @@ -1,5 +1,4 @@ -import { useAppSelector } from 'hooks'; -import { AmbiguousMonomerPreviewState, selectShowPreview } from 'state/common'; +import { AmbiguousMonomerPreviewState } from '../../../../state/common'; import { useMemo } from 'react'; import styled from '@emotion/styled'; import { @@ -12,13 +11,10 @@ import { interface Props { className?: string; + preview: AmbiguousMonomerPreviewState; } -const AmbiguousMonomerPreview = ({ className }: Props) => { - const preview = useAppSelector( - selectShowPreview, - ) as AmbiguousMonomerPreviewState; - +const AmbiguousMonomerPreview = ({ className, preview }: Props) => { const { monomer, presetMonomers, style } = preview; const isAlternatives = monomer.subtype === 'alternatives'; @@ -32,6 +28,7 @@ const AmbiguousMonomerPreview = ({ className }: Props) => { top: ${style?.top || ''}; left: ${style?.left || ''}; right: ${style?.right || ''}; + transform: ${style.transform || ''}; `; }, [style]); @@ -109,7 +106,6 @@ const AmbiguousMonomerPreview = ({ className }: Props) => { const StyledPreview = styled(AmbiguousMonomerPreview)` z-index: 5; position: absolute; - transform: translate(-50%, 0); `; export default StyledPreview; diff --git a/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx index 57b0b8b853..c7718d0d8c 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx @@ -25,7 +25,7 @@ import { import { preview } from '../../../../constants'; import styled from '@emotion/styled'; import { PresetPreviewState, selectShowPreview } from 'state/common'; -import { IconName } from 'ketcher-react/dist/components/Icon/types'; +import { IconName } from 'ketcher-react'; import useIDTAliasesTextForPreset from '../../hooks/useIDTAliasesTextForPreset'; import { useAppSelector } from 'hooks'; import IDTAliases from '../IDTAliases/IDTAliases'; diff --git a/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts b/packages/ketcher-macromolecules/src/helpers/calculatePreviewPosition.ts similarity index 69% rename from packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts rename to packages/ketcher-macromolecules/src/helpers/calculatePreviewPosition.ts index 8ca564b4c7..c190111047 100644 --- a/packages/ketcher-macromolecules/src/helpers/calculatePreviewTop.ts +++ b/packages/ketcher-macromolecules/src/helpers/calculatePreviewPosition.ts @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ -import { AmbiguousMonomerType, PolymerBond } from 'ketcher-core'; +import { AmbiguousMonomerType, PolymerBond, ZoomTool } from 'ketcher-core'; import { preview } from '../constants'; -import { PreviewStyle } from 'state/common'; +import { PreviewStyle } from '../state/types'; import assert from 'assert'; export const calculateMonomerPreviewTop = createCalculatePreviewTopFunction( @@ -24,16 +24,29 @@ export const calculateMonomerPreviewTop = createCalculatePreviewTopFunction( export const calculateNucleoElementPreviewTop = createCalculatePreviewTopFunction(preview.heightForNucleotide); -function calculateTop(target: DOMRect, height: number): number { - return target.top > height + preview.gap + preview.topPadding - ? target.top - preview.gap - height +type CalculatePreviewTopPayload = { left: number; top: number; bottom: number }; + +function calculateTop( + target: CalculatePreviewTopPayload, + height: number, +): number { + const canvasWrapperBoundingClientRect = ZoomTool.instance.canvasWrapper + .node() + ?.getBoundingClientRect(); + const canvasWrapperTopOffset = canvasWrapperBoundingClientRect?.top || 0; + + return target.top - canvasWrapperTopOffset > + height + preview.gap + preview.topPadding + ? target.top - preview.gap - height - preview.topPadding : target.bottom + preview.gap; } function createCalculatePreviewTopFunction( height: number, -): (target?: DOMRect) => string { - return function calculatePreviewTop(target?: DOMRect): string { +): (target?: CalculatePreviewTopPayload) => string { + return function calculatePreviewTop( + target?: CalculatePreviewTopPayload, + ): string { if (!target) { return ''; } @@ -45,7 +58,7 @@ function createCalculatePreviewTopFunction( const calculateAmbiguousPreviewHeight = (monomersCount: number) => { const headingHeight = 16; - const monomersHeight = 24 * monomersCount; + const monomersHeight = 35 * monomersCount; return headingHeight + monomersHeight; }; @@ -54,11 +67,27 @@ export const calculateAmbiguousMonomerPreviewTop = ( ) => { const shouldHaveOneLine = monomer.label === 'X' || monomer.label === 'N'; const monomersCount = shouldHaveOneLine ? 1 : monomer.monomers.length; - const monomersCountToUse = Math.max(5, monomersCount); + const monomersCountToUse = Math.min(5, monomersCount); const height = calculateAmbiguousPreviewHeight(monomersCountToUse); + return createCalculatePreviewTopFunction(height); }; +export function calculateAmbiguousMonomerPreviewLeft(initialLeft: number) { + const canvasWrapperBoundingClientRect = ZoomTool.instance.canvasWrapper + .node() + ?.getBoundingClientRect(); + const PREVIEW_WIDTH = 70; + const canvasWrapperRight = canvasWrapperBoundingClientRect?.right || 0; + const canvasWrapperLeft = canvasWrapperBoundingClientRect?.left || 0; + + return initialLeft + PREVIEW_WIDTH / 2 > canvasWrapperRight + ? canvasWrapperRight - PREVIEW_WIDTH + : initialLeft - PREVIEW_WIDTH / 2 < canvasWrapperLeft + ? canvasWrapperLeft + : initialLeft - PREVIEW_WIDTH / 2; +} + export const calculateBondPreviewPosition = ( bond: PolymerBond, bondCoordinates: DOMRect, diff --git a/packages/ketcher-macromolecules/src/helpers/index.ts b/packages/ketcher-macromolecules/src/helpers/index.ts index edb2555004..f5fee57313 100644 --- a/packages/ketcher-macromolecules/src/helpers/index.ts +++ b/packages/ketcher-macromolecules/src/helpers/index.ts @@ -16,6 +16,6 @@ export {}; export * from './emptyFunction'; -export * from './calculatePreviewTop'; +export * from './calculatePreviewPosition'; export * from './getPreset'; export * from './getConnectedAttachmentPoints'; diff --git a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts index d3a68f421e..c2a0103157 100644 --- a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts +++ b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts @@ -16,31 +16,18 @@ import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit'; import { + AmbiguousMonomerType, + AttachmentPointsToBonds, CoreEditor, IKetIdtAliases, MonomerItemType, PolymerBond, - AttachmentPointsToBonds, - AmbiguousMonomerType, } from 'ketcher-core'; import { RootState } from 'state'; +import { PreviewStyle, PreviewType } from 'state/types'; import { ThemeType } from 'theming/defaultTheme'; import { DeepPartial } from '../../types'; -export enum PreviewType { - Monomer = 'monomer', - Preset = 'preset', - Bond = 'bond', - AmbiguousMonomer = 'ambiguousMonomer', -} - -export interface PreviewStyle { - readonly top?: string; - readonly left?: string; - readonly right?: string; - readonly transform?: string; -} - interface BasePreviewState { readonly type: PreviewType; readonly style?: PreviewStyle; @@ -78,7 +65,7 @@ export interface AmbiguousMonomerPreviewState extends BasePreviewState { readonly presetMonomers?: ReadonlyArray; } -type EditorStatePreview = +export type EditorStatePreview = | MonomerPreviewState | PresetPreviewState | BondPreviewState diff --git a/packages/ketcher-macromolecules/src/state/index.ts b/packages/ketcher-macromolecules/src/state/index.ts index c20918a74c..47c05e7528 100644 --- a/packages/ketcher-macromolecules/src/state/index.ts +++ b/packages/ketcher-macromolecules/src/state/index.ts @@ -16,3 +16,4 @@ export * from './store'; export * from './rootSaga'; +export * from 'state/types'; diff --git a/packages/ketcher-macromolecules/src/state/types.ts b/packages/ketcher-macromolecules/src/state/types.ts new file mode 100644 index 0000000000..7399ce0692 --- /dev/null +++ b/packages/ketcher-macromolecules/src/state/types.ts @@ -0,0 +1,13 @@ +export enum PreviewType { + Monomer = 'monomer', + Preset = 'preset', + Bond = 'bond', + AmbiguousMonomer = 'ambiguousMonomer', +} + +export interface PreviewStyle { + readonly top?: string; + readonly left?: string; + readonly right?: string; + readonly transform?: string; +} diff --git a/packages/ketcher-react/src/script/ui/views/components/StructEditor/InfoPanel.tsx b/packages/ketcher-react/src/script/ui/views/components/StructEditor/InfoPanel.tsx index 385bdbf64e..e7e2084638 100644 --- a/packages/ketcher-react/src/script/ui/views/components/StructEditor/InfoPanel.tsx +++ b/packages/ketcher-react/src/script/ui/views/components/StructEditor/InfoPanel.tsx @@ -22,6 +22,8 @@ import { Struct, SGroup, CoordinateTransformation, + MonomerMicromolecule, + AmbiguousMonomer, } from 'ketcher-core'; import SGroupDataRender from './SGroupDataRender'; @@ -29,8 +31,10 @@ import { functionGroupInfoSelector } from '../../../state/functionalGroups/selec import { connect } from 'react-redux'; import clsx from 'clsx'; import { StructRender } from 'components'; - import classes from './InfoPanel.module.less'; +import { calculateAmbiguousMonomerPreviewTop } from 'src/../../ketcher-macromolecules/src/helpers/calculatePreviewPosition'; +import { PreviewType } from 'src/../../ketcher-macromolecules/src/state/types'; +import AmbiguousMonomerPreview from 'src/../../ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview'; const HOVER_PANEL_PADDING = 20; const MAX_INFO_PANEL_SIZE = 200; @@ -129,7 +133,29 @@ const InfoPanel: FC = (props) => { !SGroup.isDataSGroup(sGroup) && !SGroup.isQuerySGroup(sGroup); - return showMolecule ? ( + const isAmbiguousMonomer = + sGroup instanceof MonomerMicromolecule && + sGroup.monomer instanceof AmbiguousMonomer; + + return isAmbiguousMonomer ? ( + + ) : showMolecule ? (
Date: Sun, 1 Sep 2024 16:42:11 +0200 Subject: [PATCH 4/4] - fixed types --- .../src/EditorEvents.tsx | 17 +++--- .../RnaPresetGroup/RnaPresetGroup.tsx | 16 +++--- .../AmbiguousMonomerPreview.tsx | 5 +- .../components/BondPreview/BondPreview.tsx | 3 +- .../MonomerPreview/MonomerPreview.tsx | 3 +- .../PresetPreview/PresetPreview.tsx | 3 +- .../hooks/useIDTAliasesTextForPreset.ts | 3 +- .../src/state/common/editorSlice.ts | 56 +------------------ .../ketcher-macromolecules/src/state/types.ts | 51 +++++++++++++++++ 9 files changed, 83 insertions(+), 74 deletions(-) diff --git a/packages/ketcher-macromolecules/src/EditorEvents.tsx b/packages/ketcher-macromolecules/src/EditorEvents.tsx index 3d7315d3b7..8bd8db10c2 100644 --- a/packages/ketcher-macromolecules/src/EditorEvents.tsx +++ b/packages/ketcher-macromolecules/src/EditorEvents.tsx @@ -15,11 +15,6 @@ ***************************************************************************/ import { useCallback, useEffect } from 'react'; import { - AmbiguousMonomerPreviewState, - BondPreviewState, - MonomerPreviewState, - PresetPosition, - PresetPreviewState, selectEditor, selectEditorActiveTool, selectTool, @@ -37,6 +32,7 @@ import { BaseMonomer, Nucleoside, Nucleotide, + PolymerBond, } from 'ketcher-core'; import { calculateAmbiguousMonomerPreviewLeft, @@ -46,8 +42,15 @@ import { calculateNucleoElementPreviewTop, } from 'helpers'; import { selectAllPresets } from 'state/rna-builder'; -import { PolymerBond } from 'ketcher-core/dist/domain/entities/PolymerBond'; -import { PreviewStyle, PreviewType } from 'state/types'; +import { + AmbiguousMonomerPreviewState, + BondPreviewState, + MonomerPreviewState, + PresetPosition, + PresetPreviewState, + PreviewStyle, + PreviewType, +} from 'state/types'; const noPreviewTools = ['bond-single']; diff --git a/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx b/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx index f03864825f..889d5bbe40 100644 --- a/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx +++ b/packages/ketcher-macromolecules/src/components/monomerLibrary/RnaPresetGroup/RnaPresetGroup.tsx @@ -35,19 +35,17 @@ import { GroupContainerRow, ItemsContainer, } from 'components/monomerLibrary/monomerLibraryGroup/styles'; -import { - AmbiguousMonomerPreviewState, - PresetPosition, - PresetPreviewState, - selectEditor, - selectShowPreview, - showPreview, -} from 'state/common'; +import { selectEditor, selectShowPreview, showPreview } from 'state/common'; import { RNAContextMenu } from 'components/contextMenu/RNAContextMenu'; import { CONTEXT_MENU_ID } from 'components/contextMenu/types'; import { useContextMenu } from 'react-contexify'; import { IRnaPreset } from '../RnaBuilder/types'; -import { PreviewType } from 'state'; +import { + AmbiguousMonomerPreviewState, + PresetPosition, + PresetPreviewState, + PreviewType, +} from 'state'; export const RnaPresetGroup = ({ presets, duplicatePreset, editPreset }) => { const activePreset = useAppSelector(selectActivePreset); diff --git a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx index 2b8d6ddbc2..f76fd828bc 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/AmbiguousMonomerPreview/AmbiguousMonomerPreview.tsx @@ -1,4 +1,3 @@ -import { AmbiguousMonomerPreviewState } from '../../../../state/common'; import { useMemo } from 'react'; import styled from '@emotion/styled'; import { @@ -8,6 +7,10 @@ import { Header, RatioBar, } from './AmbiguousMonomerPreview.styles'; +// Usage of aliases instead of relative import path causes error in the following import +// because this component used directly from ketcher-react package and there are no such aliases in that package. +// It needs to create shared components library and use it in both packages. +import { AmbiguousMonomerPreviewState } from '../../../../state/types'; interface Props { className?: string; diff --git a/packages/ketcher-macromolecules/src/components/preview/components/BondPreview/BondPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/BondPreview/BondPreview.tsx index 5cd7720dcc..c4b7263f8f 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/BondPreview/BondPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/BondPreview/BondPreview.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import { useMemo } from 'react'; import { useAppSelector } from 'hooks'; -import { BondPreviewState, selectShowPreview } from 'state/common'; +import { selectShowPreview } from 'state/common'; import ConnectionOverview from 'components/shared/ConnectionOverview/ConnectionOverview'; import MonomerOverview from 'components/shared/ConnectionOverview/components/MonomerOverview/MonomerOverview'; import { useAttachmentPoints } from '../../hooks/useAttachmentPoints'; @@ -9,6 +9,7 @@ import { Container } from './BondPreview.styles'; import BondAttachmentPoints from 'components/preview/components/BondAttachmentPoints/BondAttachmentPoints'; import { preview } from '../../../../constants'; import { UsageInMacromolecule } from 'ketcher-core'; +import { BondPreviewState } from 'state'; interface Props { className?: string; diff --git a/packages/ketcher-macromolecules/src/components/preview/components/MonomerPreview/MonomerPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/MonomerPreview/MonomerPreview.tsx index 0208329fd6..df008f6367 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/MonomerPreview/MonomerPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/MonomerPreview/MonomerPreview.tsx @@ -22,7 +22,7 @@ import { StyledStructRender, } from './MonomerPreview.styles'; import styled from '@emotion/styled'; -import { MonomerPreviewState, selectShowPreview } from 'state/common'; +import { selectShowPreview } from 'state/common'; import { useAppSelector } from 'hooks'; import { useAttachmentPoints } from '../../hooks/useAttachmentPoints'; import useIDTAliasesTextForMonomer from '../../hooks/useIDTAliasesTextForMonomer'; @@ -31,6 +31,7 @@ import AttachmentPoints from '../AttachmentPoints/AttachmentPoints'; import IDTAliases from '../IDTAliases/IDTAliases'; import { preview } from '../../../../constants'; import { UsageInMacromolecule } from 'ketcher-core'; +import { MonomerPreviewState } from 'state'; interface Props { className?: string; diff --git a/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx b/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx index c7718d0d8c..5ed2c016e8 100644 --- a/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx +++ b/packages/ketcher-macromolecules/src/components/preview/components/PresetPreview/PresetPreview.tsx @@ -24,11 +24,12 @@ import { } from './PresetPreview.styles'; import { preview } from '../../../../constants'; import styled from '@emotion/styled'; -import { PresetPreviewState, selectShowPreview } from 'state/common'; +import { selectShowPreview } from 'state/common'; import { IconName } from 'ketcher-react'; import useIDTAliasesTextForPreset from '../../hooks/useIDTAliasesTextForPreset'; import { useAppSelector } from 'hooks'; import IDTAliases from '../IDTAliases/IDTAliases'; +import { PresetPreviewState } from 'state'; const icons: Extract[] = [ 'sugar', diff --git a/packages/ketcher-macromolecules/src/components/preview/hooks/useIDTAliasesTextForPreset.ts b/packages/ketcher-macromolecules/src/components/preview/hooks/useIDTAliasesTextForPreset.ts index 55a45f9af1..3022820c49 100644 --- a/packages/ketcher-macromolecules/src/components/preview/hooks/useIDTAliasesTextForPreset.ts +++ b/packages/ketcher-macromolecules/src/components/preview/hooks/useIDTAliasesTextForPreset.ts @@ -1,6 +1,7 @@ import { IKetIdtAliases } from 'ketcher-core'; import { useMemo } from 'react'; -import { PresetPosition } from 'state/common'; + +import { PresetPosition } from 'state'; type Props = { presetName: string | undefined; diff --git a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts index c2a0103157..55e42e9bc1 100644 --- a/packages/ketcher-macromolecules/src/state/common/editorSlice.ts +++ b/packages/ketcher-macromolecules/src/state/common/editorSlice.ts @@ -15,62 +15,12 @@ ***************************************************************************/ import { createSlice, PayloadAction, Slice } from '@reduxjs/toolkit'; -import { - AmbiguousMonomerType, - AttachmentPointsToBonds, - CoreEditor, - IKetIdtAliases, - MonomerItemType, - PolymerBond, -} from 'ketcher-core'; -import { RootState } from 'state'; -import { PreviewStyle, PreviewType } from 'state/types'; +import { CoreEditor } from 'ketcher-core'; +import { EditorStatePreview, RootState } from 'state'; +import { PreviewType } from 'state/types'; import { ThemeType } from 'theming/defaultTheme'; import { DeepPartial } from '../../types'; -interface BasePreviewState { - readonly type: PreviewType; - readonly style?: PreviewStyle; -} - -export interface MonomerPreviewState extends BasePreviewState { - readonly type: PreviewType.Monomer; - readonly monomer: MonomerItemType | undefined; - readonly attachmentPointsToBonds?: AttachmentPointsToBonds; -} - -export enum PresetPosition { - Library = 'library', - ChainStart = 'chainStart', - ChainMiddle = 'chainMiddle', - ChainEnd = 'chainEnd', -} - -export interface PresetPreviewState extends BasePreviewState { - readonly type: PreviewType.Preset; - readonly monomers: ReadonlyArray; - readonly position: PresetPosition; - readonly name?: string; - readonly idtAliases?: IKetIdtAliases; -} - -export interface BondPreviewState extends BasePreviewState { - readonly type: PreviewType.Bond; - readonly polymerBond: PolymerBond; -} - -export interface AmbiguousMonomerPreviewState extends BasePreviewState { - readonly type: PreviewType.AmbiguousMonomer; - readonly monomer: AmbiguousMonomerType; - readonly presetMonomers?: ReadonlyArray; -} - -export type EditorStatePreview = - | MonomerPreviewState - | PresetPreviewState - | BondPreviewState - | AmbiguousMonomerPreviewState; - // TODO: Looks like we do not use `isReady`. Delete? interface EditorState { isReady: boolean | null; diff --git a/packages/ketcher-macromolecules/src/state/types.ts b/packages/ketcher-macromolecules/src/state/types.ts index 7399ce0692..4d5e2855d8 100644 --- a/packages/ketcher-macromolecules/src/state/types.ts +++ b/packages/ketcher-macromolecules/src/state/types.ts @@ -1,3 +1,11 @@ +import { + AmbiguousMonomerType, + AttachmentPointsToBonds, + IKetIdtAliases, + MonomerItemType, + PolymerBond, +} from 'ketcher-core'; + export enum PreviewType { Monomer = 'monomer', Preset = 'preset', @@ -11,3 +19,46 @@ export interface PreviewStyle { readonly right?: string; readonly transform?: string; } + +interface BasePreviewState { + readonly type: PreviewType; + readonly style?: PreviewStyle; +} + +export interface MonomerPreviewState extends BasePreviewState { + readonly type: PreviewType.Monomer; + readonly monomer: MonomerItemType | undefined; + readonly attachmentPointsToBonds?: AttachmentPointsToBonds; +} + +export enum PresetPosition { + Library = 'library', + ChainStart = 'chainStart', + ChainMiddle = 'chainMiddle', + ChainEnd = 'chainEnd', +} + +export interface PresetPreviewState extends BasePreviewState { + readonly type: PreviewType.Preset; + readonly monomers: ReadonlyArray; + readonly position: PresetPosition; + readonly name?: string; + readonly idtAliases?: IKetIdtAliases; +} + +export interface BondPreviewState extends BasePreviewState { + readonly type: PreviewType.Bond; + readonly polymerBond: PolymerBond; +} + +export interface AmbiguousMonomerPreviewState extends BasePreviewState { + readonly type: PreviewType.AmbiguousMonomer; + readonly monomer: AmbiguousMonomerType; + readonly presetMonomers?: ReadonlyArray; +} + +export type EditorStatePreview = + | MonomerPreviewState + | PresetPreviewState + | BondPreviewState + | AmbiguousMonomerPreviewState;