Files
enviPy-bayer/static/js/ketcher2/script/ui/dialog/template-lib.jsx
2025-06-23 20:13:54 +02:00

175 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
* Copyright 2017 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
import { escapeRegExp, chunk, flow, filter as _filter, reduce, omit } from 'lodash/fp';
import { createSelector } from 'reselect';
import { h, Component } from 'preact';
import { connect } from 'preact-redux';
/** @jsx h */
import sdf from '../../chem/sdf';
import VisibleView from '../component/visibleview';
import StructRender from '../component/structrender';
import Dialog from '../component/dialog';
import SaveButton from '../component/savebutton';
import Input from '../component/input';
import SelectList from '../component/select';
import { changeFilter, changeGroup, selectTmpl, editTmpl } from '../state/templates';
import { onAction } from "../state/";
const GREEK_SIMBOLS = {
'Alpha': 'A', 'alpha': 'α',
'Beta': 'B', 'beta': 'β',
'Gamma': 'Г', 'gamma': 'γ'
};
function tmplName(tmpl, i) {
console.assert(tmpl.props && tmpl.props.group, "No group");
return tmpl.struct.name || `${tmpl.props.group} template ${i + 1}`;
}
function partition(n, array) {
console.warn('partition', n);
return chunk(n)(array);
}
const greekRe = new RegExp('\\b' + Object.keys(GREEK_SIMBOLS).join('\\b|\\b') + '\\b', 'g');
function greekify(str) {
return str.replace(greekRe, sym => GREEK_SIMBOLS[sym]);
}
const filterLibSelector = createSelector(
(props) => props.lib,
(props) => props.filter,
filterLib
);
function filterLib(lib, filter) {
console.warn('Filter', filter);
let re = new RegExp(escapeRegExp(greekify(filter)), 'i');
return flow(
_filter(item => !filter || re.test(greekify(item.struct.name)) || re.test(greekify(item.props.group))),
reduce((res, item) => {
!res[item.props.group] ? res[item.props.group] = [item] : res[item.props.group].push(item);
return res;
}, {})
)(lib)
}
const libRowsSelector = createSelector(
(props) => props.lib,
(props) => props.group,
(props) => props.COLS,
libRows
);
function libRows(lib, group, COLS) {
console.warn("Group", group);
return partition(COLS, lib[group])
}
function RenderTmpl({ tmpl, ...props }) {
return tmpl.props && tmpl.props.prerender ?
( <svg {...props}><use xlinkHref={tmpl.props.prerender}/></svg> ) :
( <StructRender struct={tmpl.struct} options={{ autoScaleMargin: 15 }} {...props}/> );
}
class TemplateLib extends Component {
select(tmpl) {
if (tmpl === this.props.selected)
this.props.onOk(this.result());
else
this.props.onSelect(tmpl);
}
result() {
const tmpl = this.props.selected;
console.assert(!tmpl || tmpl.props, 'Incorrect SDF parse');
return tmpl ? {
struct: tmpl.struct,
aid: parseInt(tmpl.props.atomid) || null,
bid: parseInt(tmpl.props.bondid) || null
} : null;
}
renderRow(row, index, COLS) {
return (
<div className="tr" key={index}>{ row.map((tmpl, i) => (
<div className={tmpl === this.props.selected ? 'td selected' : 'td'}
title={greekify(tmplName(tmpl, index * COLS + i))}>
<RenderTmpl tmpl={tmpl} className="struct" onClick={() => this.select(tmpl)}/>
<button className="attach-button" onClick={() => this.props.onAttach(tmpl)}>
Edit
</button>
</div>
))}</div>
);
}
render() {
const COLS = 3;
let { group, filter, onFilter, onChangeGroup, ...props } = this.props;
const lib = filterLibSelector(this.props);
group = lib[group] ? group : Object.keys(lib)[0];
return (
<Dialog title="Template Library"
className="template-lib" params={props}
result={() => this.result()}
buttons={[
<SaveButton className="save"
data={ sdf.stringify(this.props.lib) }
filename={'ketcher-tmpls.sdf'}>
Save To SDF
</SaveButton>,
"OK", "Cancel"]}>
<label>
<Input type="search" placeholder="Filter"
value={ filter } onChange={value => onFilter(value)}/>
</label>
<Input className="groups" component={SelectList}
splitIndexes={[Object.keys(lib).indexOf('User Templates')]}
value={ group } onChange={g => onChangeGroup(g)}
schema={{
enum: Object.keys(lib),
enumNames: Object.keys(lib).map(g => greekify(g))
}}/>
<VisibleView data={libRowsSelector({ lib, group, COLS })}
rowHeight={120} className="table">
{ (row, i) => this.renderRow(row, i, COLS) }
</VisibleView>
</Dialog>
);
}
}
export default connect(
store => ({ ...omit(['attach'], store.templates) }),
(dispatch, props) => ({
onFilter: filter => dispatch(changeFilter(filter)),
onSelect: tmpl => dispatch(selectTmpl(tmpl)),
onChangeGroup: group => dispatch(changeGroup(group)),
onAttach: tmpl => dispatch(editTmpl(tmpl)),
onOk: res => {
dispatch(onAction({ tool: 'template', opts: res }));
props.onOk(res);
}
})
)(TemplateLib);