Skip to content

Commit

Permalink
Merge branch 'master' into #1990-functional-group-does-not-connect-wi…
Browse files Browse the repository at this point in the history
…th-another-functional-group-on-clickdrag
  • Loading branch information
Stanislav Permiakov committed Feb 21, 2023
2 parents 974c5c9 + 627a160 commit d4bb199
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 56 deletions.
8 changes: 4 additions & 4 deletions packages/ketcher-core/src/application/editor/actions/erase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import { atomGetDegree } from './utils'
import { fromBondStereoUpdate } from '../actions/bond'
import { fromFragmentSplit } from './fragment'

export function fromOneAtomDeletion(restruct, id) {
return fromFragmentDeletion(restruct, { atoms: [id] })
export function fromOneAtomDeletion(restruct, atomId: number) {
return fromFragmentDeletion(restruct, { atoms: [atomId] })
}

function fromBondDeletion(restruct, bid: number, skipAtoms: Array<any> = []) {
Expand All @@ -49,7 +49,7 @@ function fromBondDeletion(restruct, bid: number, skipAtoms: Array<any> = []) {

if (
!skipAtoms.includes(bond.begin) &&
atomGetDegree(restruct, bond.begin) === 1
atomGetDegree(restruct, bond.begin) === 0
) {
if (removeAtomFromSgroupIfNeeded(action, restruct, bond.begin)) {
atomsToRemove.push(bond.begin)
Expand All @@ -60,7 +60,7 @@ function fromBondDeletion(restruct, bid: number, skipAtoms: Array<any> = []) {

if (
!skipAtoms.includes(bond.end) &&
atomGetDegree(restruct, bond.end) === 1
atomGetDegree(restruct, bond.end) === 0
) {
if (removeAtomFromSgroupIfNeeded(action, restruct, bond.end)) {
atomsToRemove.push(bond.end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ const formatProperties: FormatPropertiesMap = {
cdx: new SupportedFormatProperties(
'Base64 CDX',
ChemicalMimeType.CDX,
['.b64cdx'],
true
),
binaryCdx: new SupportedFormatProperties(
'CDX',
ChemicalMimeType.CDX,
['.cdx'],
true
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export class FormatterFactory {
case SupportedFormat.smarts:
case SupportedFormat.cdxml:
case SupportedFormat.cdx:
case SupportedFormat.binaryCdx:
case SupportedFormat.unknown:
default:
formatter = new ServerFormatter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export enum SupportedFormat {
ket = 'ket',
cdxml = 'cdxml',
cdx = 'cdx',
binaryCdx = 'binaryCdx',
unknown = 'unknown'
}

Expand Down
23 changes: 23 additions & 0 deletions packages/ketcher-core/src/utilities/b64toBlob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function b64toBlob(
b64Data: string,
contentType = '',
sliceSize = 512
): Blob {
const byteCharacters: string = window.atob(b64Data)
const byteArrays: Array<Uint8Array> = []

for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice: string = byteCharacters.slice(offset, offset + sliceSize)
const byteNumbers: Array<number> = new Array(slice.length)

for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i)
}

const byteArray: Uint8Array = new Uint8Array(byteNumbers)
byteArrays.push(byteArray)
}

const blob: Blob = new Blob(byteArrays, { type: contentType })
return blob
}
1 change: 1 addition & 0 deletions packages/ketcher-core/src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
export * from './ifDef'
export * from './tfx'
export * from './runAsyncAction'
export * from './b64toBlob'
29 changes: 3 additions & 26 deletions packages/ketcher-react/src/script/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import {
Struct,
Vec2,
fromDescriptorsAlign,
fromNewCanvas,
FunctionalGroup
fromNewCanvas
} from 'ketcher-core'
import {
DOMSubscription,
Expand All @@ -36,6 +35,7 @@ import { customOnChangeHandler } from './utils'
import { isEqual } from 'lodash/fp'
import toolMap from './tool'
import { Highlighter } from './highlighter'
import { showFunctionalGroupsTooltip } from './utils/functionalGroupsTooltip'

const SCALE = 40
const HISTORY_SIZE = 32 // put me to options
Expand Down Expand Up @@ -372,8 +372,6 @@ class Editor implements KetcherEditor {
hover(ci: any, newTool?: any, event?: PointerEvent) {
const tool = newTool || this._tool // eslint-disable-line

let infoPanelData: any = null

if (
'ci' in tool &&
(!ci || tool.ci.map !== ci.map || tool.ci.id !== ci.id)
Expand All @@ -386,28 +384,7 @@ class Editor implements KetcherEditor {

if (!event) return

const checkFunctionGroupTypes = ['sgroups', 'functionalGroups']
const closestCollapsibleStructures = this.findItem(
event,
checkFunctionGroupTypes
)
if (closestCollapsibleStructures) {
const sGroup = this.struct()?.sgroups.get(closestCollapsibleStructures.id)
if (sGroup && !sGroup.data.expanded) {
const groupName = sGroup.data.name
const groupStruct = FunctionalGroup.getFunctionalGroupByName(groupName)
infoPanelData = {
groupStruct,
event,
sGroup
}
}
}
if (infoPanelData) {
this.event.showInfo.dispatch(infoPanelData)
} else {
this.event.showInfo.dispatch(null)
}
showFunctionalGroupsTooltip(this)
}

update(action: Action | true, ignoreHistory?: boolean) {
Expand Down
15 changes: 13 additions & 2 deletions packages/ketcher-react/src/script/editor/tool/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,20 @@ class TemplateTool {
let action = null
let pasteItems

if (ci?.map === 'atoms' || ci?.map === 'functionalGroups') {
if (!ci) {
const isAddingFunctionalGroup = this.template?.molecule?.sgroups.size
if (isAddingFunctionalGroup) {
// skip, b/c we dont want to do any additional actions (e.g. rotating for s-groups)
return true
}
;[action, pasteItems] = fromTemplateOnCanvas(
restruct,
this.template,
targetPos,
angle
)
} else if (ci?.map === 'atoms' || ci?.map === 'functionalGroups') {
const atomId = getTargetAtomId(struct, ci)

;[action, pasteItems] = fromTemplateOnAtom(
restruct,
this.template,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { FunctionalGroup } from 'ketcher-core'

let showTooltipTimer: ReturnType<typeof setTimeout> | null = null

export const TOOLTIP_DELAY = 300

export function showTooltip(editor, infoPanelData) {
editor.event.showInfo.dispatch(null)

if (showTooltipTimer) {
clearTimeout(showTooltipTimer)
}
if (infoPanelData) {
showTooltipTimer = setTimeout(() => {
editor.event.showInfo.dispatch(infoPanelData)
}, TOOLTIP_DELAY)
}
}

export function showFunctionalGroupsTooltip(editor) {
let infoPanelData: any = null
const checkFunctionGroupTypes = ['sgroups', 'functionalGroups']
const closestCollapsibleStructures = editor.findItem(
event,
checkFunctionGroupTypes
)
if (closestCollapsibleStructures) {
const sGroup = editor.struct()?.sgroups.get(closestCollapsibleStructures.id)
if (sGroup && !sGroup.data.expanded) {
const groupName = sGroup.data.name
const groupStruct = FunctionalGroup.getFunctionalGroupByName(groupName)
infoPanelData = {
groupStruct,
event,
sGroup
}
}
}
showTooltip(editor, infoPanelData)
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
Struct
} from 'ketcher-core'
import templatesRawData from '../../../../templates/fg.sdf'
import { memoizedDebounce } from '../../utils'
import { TOOLTIP_DELAY } from '../../../editor/utils/functionalGroupsTooltip'

interface FGState {
lib: []
Expand Down Expand Up @@ -58,10 +60,16 @@ const highlightFGroup = (group: any) => ({
payload: group
})

export function highlightFG(dispatch, group: any) {
function notDebouncedHighlightFG(dispatch, group: any) {
dispatch(highlightFGroup(group))
}

export const highlightFG = memoizedDebounce(
notDebouncedHighlightFG,
TOOLTIP_DELAY / 3,
[0]
)

export function initFGTemplates() {
return async (dispatch) => {
const provider = FunctionalGroupsProvider.getInstance()
Expand Down
17 changes: 13 additions & 4 deletions packages/ketcher-react/src/script/ui/state/modal/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ export function generateCommonProperties(
return resultAtomAttributes
}

function castAtomPropToType(property, value) {
const typesMapping = {
charge: Number,
exactChangeFlag: Number,
unsaturatedAtom: Number
}
if (typesMapping[property]) {
return typesMapping[property](value)
}
return value
}

export function updateOnlyChangedProperties(atomId, userChangedAtom, molecule) {
const unchangedAtom = molecule.atoms.get(atomId)
const updatedKeys = Object.getOwnPropertyNames(userChangedAtom).filter(
Expand All @@ -44,10 +56,7 @@ export function updateOnlyChangedProperties(atomId, userChangedAtom, molecule) {
(updatedAtom, key) => {
const isPropertyChanged = updatedKeys.includes(key)
if (isPropertyChanged) {
updatedAtom[key] = userChangedAtom[key]
if (key === 'charge') {
updatedAtom[key] = Number(updatedAtom[key])
}
updatedAtom[key] = castAtomPropToType(key, userChangedAtom[key])
} else {
updatedAtom[key] = unchangedAtom[key]
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ketcher-react/src/script/ui/utils/fileOpener.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function fileOpener(server) {
}

function throughFileReader(file) {
const isCDX = file.name.endsWith('cdx')
const isCDX = file.name.endsWith('cdx') && !file.name.endsWith('b64cdx')
return new Promise((resolve, reject) => {
const rd = new FileReader() // eslint-disable-line no-undef

Expand Down
28 changes: 28 additions & 0 deletions packages/ketcher-react/src/script/ui/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
import _ from 'lodash'
import { escapeRegExp, filter as _filter, flow, reduce } from 'lodash/fp'
import { Option } from '../component/form/Select'

Expand Down Expand Up @@ -84,5 +85,32 @@ export const getSelectOptionsFromSchema = (schema): Array<Option> => {
}, [])
}

/**
* Creates a function, which is not called if the current argument is the same as the last one
* @param func function to be debounced
* @param delay delay in ms
* @param skipArguments indexes in arguments array to skip for comparison
* @returns debounced function, which is not called with previous argument
*/
export function memoizedDebounce(
func,
delay = 0,
skipArguments: number[] = []
) {
let lastArgs
const debouncedFunction = _.debounce(func, delay)
const getArgumentsToCompare = (args) =>
args?.filter((_, index: number) => !skipArguments.includes(index)) || []
return function (...args) {
const lastArgsToCompare = getArgumentsToCompare(lastArgs)
const argsToCompare = getArgumentsToCompare(args)
if (lastArgs && _.isEqual(argsToCompare, lastArgsToCompare)) {
return
}
lastArgs = args
debouncedFunction(...args)
}
}

export { RenderStruct } from './renderStruct'
export { fileOpener } from './fileOpener'
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import clsx from 'clsx'

import classes from './InfoPanel.module.less'

const HOVER_DELAY = 400
const HOVER_PANEL_PADDING = 20

function getSGroupFirstAtom(sGroup: SGroup, render: Render): Vec2 {
Expand Down Expand Up @@ -108,15 +107,7 @@ const InfoPanel: FC<InfoPanelProps> = (props) => {
}, [groupStruct, sGroup])

useEffect(() => {
let timer
if (groupStruct) {
timer = setTimeout(() => {
setMolecule(groupStruct.clone())
}, HOVER_DELAY)
} else {
setMolecule(null)
}
return () => clearTimeout(timer)
setMolecule(groupStruct ? groupStruct.clone() : null)
}, [groupName, groupStruct])

if (
Expand Down
Loading

0 comments on commit d4bb199

Please sign in to comment.