Skip to content

Commit

Permalink
#437 Add possibility to save structure as PNG
Browse files Browse the repository at this point in the history
  • Loading branch information
StartsevDS committed Apr 14, 2021
1 parent 97c408a commit c53b4cf
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const SaveButton = props => {
const {
server,
filename = 'unnamed',
outputFormat,
data,
type,
mode = 'saveFile',
Expand Down Expand Up @@ -59,7 +60,7 @@ const SaveButton = props => {
const saveImage = () => {
const ketcherInstance = getKetcherInstance()
ketcherInstance
.generateImageAsync(data, { outputFormat: 'svg' })
.generateImageAsync(data, { outputFormat })
.then(blob => {
saveAs(blob, filename)
onSave()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
getPropertiesByFormat,
formatProperties
} from 'ketcher-core'

import { molfileManager } from '../../../../../../chem/molfile'
import smilesManager from '../../../../../../chem/smiles'
import graphManager from '../../../../../../format/chemGraph'
import * as structFormat from '../../../../../data/convert/structConverter'
import { saveUserTmpl } from '../../../../../state/templates'
Expand All @@ -29,6 +32,8 @@ import { check } from '../../../../../state/server'
import { Dialog } from '../../../../components'
import Form, { Field } from '../../../../../component/form/form'
import SaveButton from '../../../../../component/view/savebutton'
import Tabs from '../../../../../component/view/Tabs'
import SaveImageTab from './SaveImageTab'

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

Expand Down Expand Up @@ -60,9 +65,10 @@ const saveSchema = {
class SaveDialog extends Component {
constructor(props) {
super(props)
this.state = { disableControls: false }
this.state = { imageFormat: 'svg', tabIndex: 0 }
this.isRxn = this.props.struct.hasRxnArrow()
this.textAreaRef = createRef()
this.formContainerRef = createRef()
const formats = [this.isRxn ? 'rxn' : 'mol', 'smiles', 'graph']
if (this.props.server)
formats.push(
Expand Down Expand Up @@ -91,6 +97,13 @@ class SaveDialog extends Component {
componentDidMount() {
const { checkOptions } = this.props.checkState
this.props.onCheck(checkOptions)
if (this.formContainerRef.current) {
const {
width,
height
} = this.formContainerRef.current.getBoundingClientRect()
this.setState({ dimensions: { width, height } })
}
}

showStructWarningMessage = format => {
Expand All @@ -99,35 +112,33 @@ class SaveDialog extends Component {
}

changeType = type => {
this.setState({ disableControls: true })

const { struct, server, options, formState } = this.props

const factory = new FormatterFactory(server, graphManager)
const factory = new FormatterFactory(
server,
graphManager,
molfileManager,
smilesManager
)

const service = factory.create(type, options)

return service
.getStructureFromStructAsync(struct)
.then(
structStr => {
this.setState({ structStr })
setTimeout(() => {
if (this.textAreaRef.current) {
this.textAreaRef.current.select()
}
}, 10) // TODO: remove hack
},
e => {
//TODO: add error handler call
alert(e.message)
this.props.onResetForm(formState)
return e
}
)
.finally(() => {
this.setState({ disableControls: false })
})
return service.getStructureFromStructAsync(struct).then(
structStr => {
this.setState({ structStr })
setTimeout(() => {
if (this.textAreaRef.current) {
this.textAreaRef.current.select()
}
}, 10) // TODO: remove hack
},
e => {
//TODO: add error handler call
alert(e.message)
this.props.onResetForm(formState)
return e
}
)
}

getWarnings = format => {
Expand Down Expand Up @@ -157,74 +168,118 @@ class SaveDialog extends Component {
return warnings
}

render() {
const { structStr, disableControls } = this.state
changeTab = tabIndex => {
this.setState({ tabIndex })
}

changeImageFormat = imageFormat => {
this.setState({ imageFormat })
}

renderSaveFile = () => {
const formState = Object.assign({}, this.props.formState)
delete formState.moleculeErrors
const { filename, format } = formState.result
const warnings = this.getWarnings(format)
const { structStr } = this.state
return (
<div className={classes.form_container} ref={this.formContainerRef}>
<Form
schema={this.saveSchema}
init={{
filename,
format: this.isRxn ? 'rxn' : 'mol'
}}
{...formState}>
<Field name="filename" />
<Field name="format" onChange={this.changeType} />
</Form>
<textarea value={structStr} readOnly ref={this.textAreaRef} />
{warnings.map(warning => (
<div className={classes.warnings_container}>
<div className={classes.warning} />
<div className={classes.warnings_arr}>{warning}</div>
</div>
))}
</div>
)
}

getButtons = () => {
const { imageFormat, structStr, tabIndex } = this.state
const formState = this.props.formState
const { filename, format } = formState.result
const isCleanStruct = this.props.struct.isBlank()
const buttons = [
[
<SaveButton
className="save-button"
mode="saveFile"
data={structStr}
filename={filename + getPropertiesByFormat(format).extensions[0]}
key="save-file-button"
type={format.mime}
server={this.props.server}
onSave={this.props.onOk}
disabled={!formState.valid || isCleanStruct}>
Save To File…
</SaveButton>,
<button
className="save-button"
key="save-tmpl"
disabled={isCleanStruct}
onClick={() => this.props.onTmplSave(this.props.struct)}>
Save to Templates...
</button>,
'Close'
],
[
<SaveButton
className="save-button"
mode="saveImage"
data={structStr}
filename={filename}
outputFormat={imageFormat}
key="save-image-button"
type="image/svg+xml"
onSave={this.props.onOk}
disabled={!formState.valid || isCleanStruct || !this.props.server}>
Save
</SaveButton>,
'Close'
]
]
return buttons[tabIndex]
}

render() {
const tabs = [
{
caption: 'Structure',
component: this.renderSaveFile
},
{
caption: 'Image',
component: SaveImageTab,
props: {
changeImageFormat: this.changeImageFormat,
dimensions: this.state.dimensions
}
}
]
return (
<Dialog
title="Save Structure"
className={classes.save}
params={this.props}
buttons={[
<SaveButton
mode="saveFile"
data={structStr}
filename={filename + getPropertiesByFormat(format).extensions[0]}
key="save-file-button"
type={format.mime}
server={this.props.server}
onSave={() => this.props.onOk()}
disabled={disableControls || !formState.valid || isCleanStruct}>
Save To File…
</SaveButton>,
<SaveButton
mode="saveImage"
data={structStr}
filename={filename}
key="save-image-button"
type="image/svg+xml"
server={this.props.server}
onSave={this.props.onOk}
disabled={
disableControls ||
!formState.valid ||
isCleanStruct ||
!this.props.server
}>
Save As Image...
</SaveButton>,
<button
key="save-tmpl"
disabled={disableControls || isCleanStruct}
onClick={() => this.props.onTmplSave(this.props.struct)}>
Save to Templates
</button>,
'Close'
]}>
<div className={classes.form_container}>
<Form
schema={this.saveSchema}
init={{
filename,
format: this.isRxn ? 'rxn' : 'mol'
}}
{...formState}>
<Field name="filename" />
<Field name="format" onChange={this.changeType} />
</Form>
<textarea value={structStr} readOnly ref={this.textAreaRef} />
{warnings.map(warning => (
<div className={classes.warnings_container}>
<div className={classes.warning} />
<div className={classes.warnings_arr}>{warning}</div>
</div>
))}
</div>
buttons={this.getButtons()}>
<Tabs
tabs={tabs}
changeTab={tab => {
this.changeTab(tab)
}}
captions={this.tabs}
/>
</Dialog>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,4 @@
cursor: text;
resize: both;
}

footer {
display: flex;
justify-content: space-around;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'

const SaveImageTab = ({ dimensions, changeImageFormat }) => {
const formats = [
{ extension: 'svg', text: 'SVG Document' },
{ extension: 'png', text: 'PNG Image' }
]

const renderOptions = formats => {
return formats.map(format => {
const { extension, text } = format
return (
<option key={extension} value={extension}>
{text}
</option>
)
})
}

const handleChange = ({ target }) => {
changeImageFormat(target.value)
}

return (
<div style={dimensions}>
<label>
Image Format: &nbsp;
<select onChange={handleChange}>{renderOptions(formats)}</select>
</label>
</div>
)
}

export default SaveImageTab

0 comments on commit c53b4cf

Please sign in to comment.