chem-generic-ui 0.1.44 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +11 -0
- package/.eslintrc +23 -0
- package/.tool-versions +3 -0
- package/chem-generic-ui-v0.1.41.tgz +0 -0
- package/dist/bundle.js +1 -1
- package/dist/bundle.js.LICENSE.txt +70 -0
- package/dist/ds_details.json +57 -0
- package/dist/ds_klass.json +102 -0
- package/dist/ds_props.json +54 -0
- package/dist/index.html +14 -0
- package/dist/sg_details.json +2036 -0
- package/dist/sg_klass.json +850 -0
- package/dist/units_system.json +430 -0
- package/package.json +3 -13
- package/public/ds_details.json +57 -0
- package/public/ds_klass.json +102 -0
- package/public/ds_props.json +54 -0
- package/public/favicon.ico +0 -0
- package/public/images/not_available.svg +1 -0
- package/public/index.html +47 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/public/sg_details.json +2036 -0
- package/public/sg_klass.json +850 -0
- package/public/test/ds_props.json +54 -0
- package/public/units_system.json +430 -0
- package/src/app.js +2 -0
- package/src/asserts/bootstrap-theme.min.css +6 -0
- package/src/asserts/bootstrap.min.css +6 -0
- package/src/asserts/main.css +458 -0
- package/src/asserts/main.scss +490 -0
- package/src/components/admin/ElementManager.js +28 -0
- package/src/components/details/GenDSDetails.js +164 -0
- package/src/components/details/GenSgDetails.js +396 -0
- package/src/components/dnd/DragDropItemTypes.js +13 -0
- package/src/components/dnd/GenericElDropTarget.js +160 -0
- package/src/components/dnd/GridDnD.js +42 -0
- package/src/components/dnd/PanelDnD.js +85 -0
- package/src/components/fields/ButtonConfirm.js +45 -0
- package/src/components/fields/ButtonTooltip.js +46 -0
- package/src/components/fields/FieldLabel.js +18 -0
- package/src/components/fields/GenDSMisType.js +20 -0
- package/src/components/fields/GenFormGroupCb.js +17 -0
- package/src/components/fields/GenProperties.js +56 -0
- package/src/components/fields/GenPropertiesFields.js +440 -0
- package/src/components/layers/GenPropertiesLayer.js +178 -0
- package/src/components/layers/LayerModal.js +52 -0
- package/src/components/layers/LayersLayout.js +68 -0
- package/src/components/models/Attachment.js +37 -0
- package/src/components/models/GenericSubField.js +10 -0
- package/src/components/table/DropLinkRenderer.js +35 -0
- package/src/components/table/DropRenderer.js +31 -0
- package/src/components/table/DropTextRenderer.js +25 -0
- package/src/components/table/GenericElTableDropTarget.js +131 -0
- package/src/components/table/GridBtn.js +41 -0
- package/src/components/table/GridEntry.js +75 -0
- package/src/components/table/SamOption.js +53 -0
- package/src/components/table/SelectRenderer.js +34 -0
- package/src/components/table/TableRecord.js +254 -0
- package/src/components/table/UConverterRenderer.js +24 -0
- package/src/components/tools/collate.js +65 -0
- package/src/components/tools/orten.js +171 -0
- package/src/components/tools/utils.js +414 -0
- package/src/data/SystemUnits.js +434 -0
- package/src/data/systemUnits.json +430 -0
- package/src/index.css +13 -0
- package/src/index.html +1 -0
- package/src/index.js +45 -0
- package/src/logo.svg +1 -0
- package/src/simulations/SimuDS.js +52 -0
- package/src/simulations/SimuSG.js +54 -0
- package/webpack.config.js +46 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { sortBy } from 'lodash';
|
|
3
|
+
import GenPropertiesLayer from './GenPropertiesLayer';
|
|
4
|
+
|
|
5
|
+
const LayersLayout = (
|
|
6
|
+
layers, options, funcChange,
|
|
7
|
+
funcSubChange = () => {}, funcClick = () => {}, layout = [], id = 0, isPreview = false,
|
|
8
|
+
activeWF = false
|
|
9
|
+
) => {
|
|
10
|
+
const sortedLayers = sortBy(layers, ['position', 'wf_position']) || [];
|
|
11
|
+
sortedLayers.forEach((layer, idx) => {
|
|
12
|
+
const uk = `${layer.key}_${idx}`;
|
|
13
|
+
if (typeof layer.cond_fields === 'undefined' || layer.cond_fields == null || layer.cond_fields.length === 0) {
|
|
14
|
+
const ig = (
|
|
15
|
+
<GenPropertiesLayer
|
|
16
|
+
id={id}
|
|
17
|
+
key={uk}
|
|
18
|
+
layer={layer}
|
|
19
|
+
onChange={funcChange}
|
|
20
|
+
onSubChange={funcSubChange}
|
|
21
|
+
selectOptions={options}
|
|
22
|
+
onClick={funcClick}
|
|
23
|
+
layers={layers}
|
|
24
|
+
isPreview={isPreview}
|
|
25
|
+
activeWF={activeWF}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
layout.push(ig);
|
|
29
|
+
} else if (layer.cond_fields && layer.cond_fields.length > 0) {
|
|
30
|
+
let showLayer = false;
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < layer.cond_fields.length; i += 1) {
|
|
33
|
+
const cond = layer.cond_fields[i] || {};
|
|
34
|
+
const fd = ((layers[cond.layer] || {}).fields || [])
|
|
35
|
+
.find(f => f.field === cond.field) || {};
|
|
36
|
+
if (fd.type === 'checkbox' && ((['false', 'no', 'f', '0'].includes((cond.value || '').trim().toLowerCase()) && (typeof (fd && fd.value) === 'undefined' || fd.value === false)) ||
|
|
37
|
+
(['true', 'yes', 't', '1'].includes((cond.value || '').trim().toLowerCase()) && (typeof fd.value !== 'undefined' && fd.value === true)))) {
|
|
38
|
+
showLayer = true;
|
|
39
|
+
break;
|
|
40
|
+
} else if (['text', 'select'].includes(fd.type) && (typeof (fd && fd.value) !== 'undefined' && (fd.value || '').trim() === (cond.value || '').trim())) {
|
|
41
|
+
showLayer = true;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (showLayer === true) {
|
|
47
|
+
const igs = (
|
|
48
|
+
<GenPropertiesLayer
|
|
49
|
+
id={id}
|
|
50
|
+
key={uk}
|
|
51
|
+
layer={layer}
|
|
52
|
+
onChange={funcChange}
|
|
53
|
+
onSubChange={funcSubChange}
|
|
54
|
+
selectOptions={options}
|
|
55
|
+
onClick={funcClick}
|
|
56
|
+
layers={layers}
|
|
57
|
+
isPreview={isPreview}
|
|
58
|
+
activeWF={activeWF}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
layout.push(igs);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return layout;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default LayersLayout;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
|
|
3
|
+
export default class Attachment {
|
|
4
|
+
constructor(args) {
|
|
5
|
+
Object.assign(this, args);
|
|
6
|
+
if (!this.id) { this.id = Attachment.buildID(); }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static buildID() { return uuid(); }
|
|
10
|
+
|
|
11
|
+
static fromFile(file) {
|
|
12
|
+
return new Attachment({
|
|
13
|
+
file,
|
|
14
|
+
name: file.name,
|
|
15
|
+
filename: file.name,
|
|
16
|
+
identifier: file.id,
|
|
17
|
+
is_deleted: false,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get isNew() {
|
|
22
|
+
return this.is_new === true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
serialize() {
|
|
26
|
+
return super.serialize({
|
|
27
|
+
filename: this.filename,
|
|
28
|
+
identifier: this.identifier,
|
|
29
|
+
file: this.file,
|
|
30
|
+
thumb: this.thumb,
|
|
31
|
+
content_type: this.content_type,
|
|
32
|
+
is_deleted: this.is_deleted,
|
|
33
|
+
id: this.id,
|
|
34
|
+
is_new: this.isNew || false
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
2
|
+
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
|
3
|
+
/* eslint-disable jsx-a11y/interactive-supports-focus */
|
|
4
|
+
/* eslint-disable react/forbid-prop-types */
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import PropTypes from 'prop-types';
|
|
7
|
+
// import Aviator from 'aviator';
|
|
8
|
+
// import UIStore from '../stores/UIStore';
|
|
9
|
+
|
|
10
|
+
// const linkSample = (type, id) => {
|
|
11
|
+
// const { currentCollection, isSync } = UIStore.getState();
|
|
12
|
+
// const collectionUrl = (!isNaN(id)) ?
|
|
13
|
+
// `${currentCollection.id}/${type}/${id}` : `${currentCollection.id}/${type}`;
|
|
14
|
+
// Aviator.navigate(isSync ? `/scollection/${collectionUrl}` : `/collection/${collectionUrl}`);
|
|
15
|
+
// };
|
|
16
|
+
|
|
17
|
+
const DropLinkRenderer = (props) => {
|
|
18
|
+
const { sField, node, onLink } = props;
|
|
19
|
+
const dId = ((node.data[sField.id] || {}).value || {}).el_id || '';
|
|
20
|
+
const dVal = ((node.data[sField.id] || {}).value || {}).el_short_label || ' ';
|
|
21
|
+
if (dId === '') return <div />;
|
|
22
|
+
return (
|
|
23
|
+
<a role="link" onClick={() => onLink('sample', dId)} style={{ cursor: 'pointer' }}>
|
|
24
|
+
<span className="reaction-material-link">{dVal}</span>
|
|
25
|
+
</a>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
DropLinkRenderer.propTypes = {
|
|
30
|
+
sField: PropTypes.object.isRequired,
|
|
31
|
+
node: PropTypes.object.isRequired,
|
|
32
|
+
onLink: PropTypes.func.isRequired
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default DropLinkRenderer;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { cloneDeep } from 'lodash';
|
|
5
|
+
import GenericElTableDropTarget from './GenericElTableDropTarget';
|
|
6
|
+
|
|
7
|
+
const DropRenderer = (props) => {
|
|
8
|
+
const {
|
|
9
|
+
opt, sField, onChange, node
|
|
10
|
+
} = props;
|
|
11
|
+
if (!['drag_molecule', 'drag_sample'].includes(sField.type)) return null;
|
|
12
|
+
const { data } = node;
|
|
13
|
+
opt.dndItems = [sField.type.split('_')[1]];
|
|
14
|
+
opt.sField = sField;
|
|
15
|
+
opt.data = data;
|
|
16
|
+
const oopt = cloneDeep(opt);
|
|
17
|
+
return (
|
|
18
|
+
<div className="drop_generic_properties drop_generic_table_wrap">
|
|
19
|
+
<GenericElTableDropTarget opt={oopt} onDrop={onChange} />
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
DropRenderer.propTypes = {
|
|
25
|
+
sField: PropTypes.object.isRequired,
|
|
26
|
+
opt: PropTypes.object.isRequired,
|
|
27
|
+
onChange: PropTypes.func.isRequired,
|
|
28
|
+
node: PropTypes.object.isRequired
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default DropRenderer;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
|
5
|
+
|
|
6
|
+
const DropTextRenderer = (props) => {
|
|
7
|
+
const { attr, sField, node } = props;
|
|
8
|
+
let displayValue = ((node.data[sField.id] || {}).value || {})[`el_${attr.value}`] || '';
|
|
9
|
+
displayValue = (attr.value === 'molecular_weight' && displayValue !== '') ? displayValue.toFixed(6) : displayValue;
|
|
10
|
+
return (
|
|
11
|
+
<OverlayTrigger placement="top" overlay={<Tooltip id="copy_clipboard">copy to clipboard</Tooltip>}>
|
|
12
|
+
<div role="button" data-clipboard-text={displayValue} className="clipboardBtn" style={{ wordBreak: 'break-all', cursor: 'copy' }}>
|
|
13
|
+
{displayValue}
|
|
14
|
+
</div>
|
|
15
|
+
</OverlayTrigger>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
DropTextRenderer.propTypes = {
|
|
20
|
+
attr: PropTypes.object.isRequired,
|
|
21
|
+
sField: PropTypes.object.isRequired,
|
|
22
|
+
node: PropTypes.object.isRequired
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default DropTextRenderer;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
/* eslint-disable react/forbid-prop-types */
|
|
3
|
+
import React, { Component } from 'react';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { v4 as uuid } from 'uuid';
|
|
6
|
+
import { DropTarget } from 'react-dnd';
|
|
7
|
+
import { Tooltip, OverlayTrigger, Popover, Button } from 'react-bootstrap';
|
|
8
|
+
|
|
9
|
+
const base = (opt, iconClass, onDrop = () => {}, params = {}) => {
|
|
10
|
+
const noSvg = '/images/not_available.svg';
|
|
11
|
+
if (opt.value && opt.value.el_id) {
|
|
12
|
+
const label = opt.value.el_label;
|
|
13
|
+
const svg = (opt.value.el_svg && opt.value.el_svg.endsWith('.svg')) ? opt.value.el_svg : noSvg;
|
|
14
|
+
const pop = (
|
|
15
|
+
<Popover id="popover-svg" title={label} style={{ maxWidth: 'none', maxHeight: 'none' }}>
|
|
16
|
+
<img src={svg} style={{ height: '26vh', width: '26vh' }} alt="" />
|
|
17
|
+
</Popover>
|
|
18
|
+
);
|
|
19
|
+
const simg = (path, txt) => ((path && path !== '') ? (
|
|
20
|
+
<div className="s-img">
|
|
21
|
+
<OverlayTrigger delayShow={1000} trigger={['hover']} placement="top" rootClose onHide={null} overlay={pop}>
|
|
22
|
+
<img className="generic_grid_img" src={path} alt="" />
|
|
23
|
+
</OverlayTrigger>
|
|
24
|
+
<div className="del_btn">
|
|
25
|
+
<OverlayTrigger delayShow={1000} placement="top" overlay={<Tooltip id={uuid()}>remove this molecule</Tooltip>}>
|
|
26
|
+
<Button className="btn_del" bsSize="xsmall" onClick={() => onDrop({}, params)} ><i className="fa fa-trash-o" aria-hidden="true" /></Button>
|
|
27
|
+
</OverlayTrigger>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
) : (<div className="data" style={{ width: '4vw' }}>{txt}</div>));
|
|
31
|
+
return simg(svg, label);
|
|
32
|
+
}
|
|
33
|
+
return (<span className={`icon-${iconClass} indicator`} style={{ width: '4vw' }} />);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const show = (opt, iconClass, onDrop) => {
|
|
37
|
+
if (opt.type === 'table') {
|
|
38
|
+
const sField = opt.sField || {};
|
|
39
|
+
const subVal = opt.data[sField.id];
|
|
40
|
+
const { data } = opt;
|
|
41
|
+
return base(subVal, iconClass, onDrop, { sField, data });
|
|
42
|
+
}
|
|
43
|
+
return base(opt, iconClass);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const source = (type, props, id) => {
|
|
47
|
+
let isAssoc = false;
|
|
48
|
+
const taggable = (props && props.tag && props.tag.taggable_data) || {};
|
|
49
|
+
if (taggable.element && taggable.element.id === id) {
|
|
50
|
+
isAssoc = false;
|
|
51
|
+
} else {
|
|
52
|
+
isAssoc = !!(taggable.reaction_id || taggable.wellplate_id || taggable.element);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (type) {
|
|
56
|
+
case 'molecule':
|
|
57
|
+
return {
|
|
58
|
+
el_id: props.molecule.id,
|
|
59
|
+
el_type: 'molecule',
|
|
60
|
+
el_label: props.molecule.cano_smiles || props.molecule_formula || props.molecule_name_label,
|
|
61
|
+
el_inchikey: props.molecule.inchikey,
|
|
62
|
+
el_smiles: props.molecule.cano_smiles,
|
|
63
|
+
el_iupac: props.molecule.iupac_name,
|
|
64
|
+
el_molecular_weight: props.molecule.molecular_weight,
|
|
65
|
+
el_svg: `/images/molecules/${props.molecule.molecule_svg_file}`,
|
|
66
|
+
};
|
|
67
|
+
case 'sample':
|
|
68
|
+
return {
|
|
69
|
+
el_id: props.id,
|
|
70
|
+
is_new: true,
|
|
71
|
+
cr_opt: 1,
|
|
72
|
+
isAssoc,
|
|
73
|
+
el_type: 'sample',
|
|
74
|
+
el_label: props.short_label,
|
|
75
|
+
el_short_label: props.short_label,
|
|
76
|
+
el_name: props.name,
|
|
77
|
+
el_external_label: props.external_label,
|
|
78
|
+
el_molecular_weight: props.molecule_molecular_weight,
|
|
79
|
+
el_svg: props.sample_svg_file ? `/images/samples/${props.sample_svg_file}` : `/images/molecules/${props.molecule.molecule_svg_file}`,
|
|
80
|
+
};
|
|
81
|
+
default:
|
|
82
|
+
return {
|
|
83
|
+
el_id: props.id,
|
|
84
|
+
is_new: true,
|
|
85
|
+
cr_opt: 0,
|
|
86
|
+
el_type: props.type,
|
|
87
|
+
el_label: props.short_label,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const dropTarget = {
|
|
93
|
+
drop(targetProps, monitor) {
|
|
94
|
+
const sourceProps = monitor.getItem().element;
|
|
95
|
+
const type = targetProps.opt.sField.type.split('_')[1];
|
|
96
|
+
const sourceTag = source(type, sourceProps, targetProps.opt.id);
|
|
97
|
+
targetProps.onDrop(sourceTag, targetProps.opt);
|
|
98
|
+
},
|
|
99
|
+
canDrop(targetProps, monitor) {
|
|
100
|
+
return true;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const dropCollect = (connect, monitor) => ({
|
|
105
|
+
connectDropTarget: connect.dropTarget(),
|
|
106
|
+
isOver: monitor.isOver(),
|
|
107
|
+
canDrop: monitor.canDrop()
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
class GenericElTableDropTarget extends Component {
|
|
111
|
+
render() {
|
|
112
|
+
const {
|
|
113
|
+
connectDropTarget, isOver, canDrop, opt, onDrop
|
|
114
|
+
} = this.props;
|
|
115
|
+
const className = `target${isOver ? ' is-over' : ''}${canDrop ? ' can-drop' : ''}`;
|
|
116
|
+
return connectDropTarget(<div className={className} style={{ display: 'inline-flex', justifyContent: 'center' }}>{show(opt, 'sample', onDrop)}</div>);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default
|
|
121
|
+
DropTarget(props => props.opt.dndItems, dropTarget, dropCollect)(GenericElTableDropTarget);
|
|
122
|
+
|
|
123
|
+
GenericElTableDropTarget.propTypes = {
|
|
124
|
+
connectDropTarget: PropTypes.func.isRequired,
|
|
125
|
+
isOver: PropTypes.bool.isRequired,
|
|
126
|
+
canDrop: PropTypes.bool.isRequired,
|
|
127
|
+
opt: PropTypes.object.isRequired,
|
|
128
|
+
onDrop: PropTypes.func
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
GenericElTableDropTarget.defaultProps = { onDrop: () => {} };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
|
6
|
+
import GridDnD from '../dnd/GridDnD';
|
|
7
|
+
|
|
8
|
+
const AddRowBtn = ({ addRow }) => (
|
|
9
|
+
<OverlayTrigger delayShow={1000} placement="top" overlay={<Tooltip id={uuid()}>add entry</Tooltip>}>
|
|
10
|
+
<Button onClick={() => addRow()} bsSize="xsmall" bsStyle="primary"><i className="fa fa-plus" aria-hidden="true" /></Button>
|
|
11
|
+
</OverlayTrigger>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
AddRowBtn.propTypes = { addRow: PropTypes.func.isRequired };
|
|
15
|
+
|
|
16
|
+
const DelRowBtn = ({ delRow, node }) => {
|
|
17
|
+
const { data } = node;
|
|
18
|
+
return (
|
|
19
|
+
<OverlayTrigger delayShow={1000} placement="top" overlay={<Tooltip id={uuid()}>remove</Tooltip>}>
|
|
20
|
+
<Button onClick={() => delRow(data)} bsSize="xsmall"><i className="fa fa-times" aria-hidden="true" /></Button>
|
|
21
|
+
</OverlayTrigger>);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
DelRowBtn.propTypes = { delRow: PropTypes.func.isRequired, node: PropTypes.object.isRequired };
|
|
25
|
+
|
|
26
|
+
const NullRowBtn = () => (<div className="grid-btn-none"><span className="fa fa-arrows" /></div>);
|
|
27
|
+
|
|
28
|
+
const DnDRowBtn = ({
|
|
29
|
+
moveRow, field, type, node
|
|
30
|
+
}) => (
|
|
31
|
+
<GridDnD field={field} type={type} rowValue={node.data} handleMove={moveRow} />
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
DnDRowBtn.propTypes = {
|
|
35
|
+
moveRow: PropTypes.func.isRequired,
|
|
36
|
+
field: PropTypes.string.isRequired,
|
|
37
|
+
type: PropTypes.string.isRequired,
|
|
38
|
+
node: PropTypes.object.isRequired
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { AddRowBtn, DelRowBtn, DnDRowBtn, NullRowBtn };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FormControl, InputGroup } from 'react-bootstrap';
|
|
4
|
+
|
|
5
|
+
const setCell = (columnDef, rowValue) => {
|
|
6
|
+
const {
|
|
7
|
+
type, field, onCellChange, cellRenderer, cellParams
|
|
8
|
+
} = columnDef;
|
|
9
|
+
switch (type) {
|
|
10
|
+
case 'text':
|
|
11
|
+
return (
|
|
12
|
+
<FormControl type="text" value={rowValue[field] || ''} onChange={e => onCellChange({ e, columnDef, rowValue })} />
|
|
13
|
+
);
|
|
14
|
+
case 'system-defined':
|
|
15
|
+
return (
|
|
16
|
+
<InputGroup>
|
|
17
|
+
<FormControl type="number" value={rowValue[field].value || ''} onChange={e => onCellChange({ e, columnDef, rowValue })} />
|
|
18
|
+
<InputGroup.Button>
|
|
19
|
+
{cellRenderer({ ...cellParams, node: { data: rowValue } })}
|
|
20
|
+
</InputGroup.Button>
|
|
21
|
+
</InputGroup>
|
|
22
|
+
);
|
|
23
|
+
case 'select':
|
|
24
|
+
case 'drag_molecule':
|
|
25
|
+
case 'drag_sample':
|
|
26
|
+
case 'dnd':
|
|
27
|
+
return (cellRenderer({ ...cellParams, node: { data: rowValue } }));
|
|
28
|
+
default:
|
|
29
|
+
return <span />;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const ColumnHeader = (columnDefs) => {
|
|
34
|
+
const ch = [];
|
|
35
|
+
const h = (col, idx) => {
|
|
36
|
+
const {
|
|
37
|
+
width, headerName, headerComponent, headerParams
|
|
38
|
+
} = col;
|
|
39
|
+
const colCss = {};
|
|
40
|
+
if (width) { Object.assign(colCss, { width, minWidth: width }); }
|
|
41
|
+
return (
|
|
42
|
+
<div key={`column_header_${col.colId || col.field}_${idx}`} className="generic_grid_header" style={colCss}>
|
|
43
|
+
{headerComponent ? headerComponent(headerParams) : null}
|
|
44
|
+
<div style={colCss}>{headerName}</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
columnDefs.forEach((col, idx) => ch.push(h(col, idx)));
|
|
49
|
+
return (<div className="generic_grid" style={{ borderBottom: '1px solid #ccc' }}><div>{ch}</div></div>);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ColumnRow = (columnDefs, rowValue) => {
|
|
53
|
+
const ch = [];
|
|
54
|
+
const h = (col, val, idx) => {
|
|
55
|
+
const {
|
|
56
|
+
field, width, cellParams, cellRenderer
|
|
57
|
+
} = col;
|
|
58
|
+
const colCss = {};
|
|
59
|
+
if (width) { Object.assign(colCss, { width, minWidth: width }); }
|
|
60
|
+
return (
|
|
61
|
+
<div key={`column_row_${val.id}_${col.colId || col.field}_${idx}`} className="generic_grid_row" style={colCss}>
|
|
62
|
+
{field ? (setCell(col, val) || '') : (cellRenderer({ ...cellParams, node: { data: val } }) || '')}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
columnDefs.forEach((col, idx) => ch.push(h(col, rowValue, idx)));
|
|
67
|
+
return (<div key={`column_row_${rowValue.id}`} className="generic_grid"><div>{ch}</div></div>);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const NoRow = (values) => {
|
|
71
|
+
if (values && values.length > 0) return null;
|
|
72
|
+
return (<div className="generic_grid"><div><div className="generic_grid_row" style={{ width: '100%' }}>(No data)</div></div></div>);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export { ColumnHeader, ColumnRow, NoRow };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { v4 as uuid } from 'uuid';
|
|
5
|
+
import { OverlayTrigger, Radio, Tooltip } from 'react-bootstrap';
|
|
6
|
+
|
|
7
|
+
const SamOption = (props) => {
|
|
8
|
+
const { sField, node, onChange } = props;
|
|
9
|
+
const { data } = node;
|
|
10
|
+
const fValue = (data[sField.id] && data[sField.id].value) || {};
|
|
11
|
+
if (!fValue.is_new) return <div />;
|
|
12
|
+
const rUUID = uuid();
|
|
13
|
+
return (
|
|
14
|
+
<div className="generic_sam_options">
|
|
15
|
+
<OverlayTrigger delayShow={1000} placement="right" overlay={<Tooltip id={uuid()}>associate with this sample</Tooltip>}>
|
|
16
|
+
<Radio
|
|
17
|
+
name={`dropS_${rUUID}`}
|
|
18
|
+
disabled={fValue.isAssoc}
|
|
19
|
+
checked={fValue.cr_opt === 0}
|
|
20
|
+
onChange={() => onChange({ node, subField: sField, crOpt: 0 })}
|
|
21
|
+
>
|
|
22
|
+
Current
|
|
23
|
+
</Radio>
|
|
24
|
+
</OverlayTrigger>
|
|
25
|
+
<OverlayTrigger delayShow={1000} placement="right" overlay={<Tooltip id={uuid()}>split from the sample first and then associate with it</Tooltip>}>
|
|
26
|
+
<Radio
|
|
27
|
+
name={`dropS_${rUUID}`}
|
|
28
|
+
checked={fValue.cr_opt === 1}
|
|
29
|
+
onChange={() => onChange({ node, subField: sField, crOpt: 1 })}
|
|
30
|
+
>
|
|
31
|
+
Split
|
|
32
|
+
</Radio>
|
|
33
|
+
</OverlayTrigger>
|
|
34
|
+
<OverlayTrigger delayShow={1000} placement="right" overlay={<Tooltip id={uuid()}>duplicate the sample first and then associate with it</Tooltip>}>
|
|
35
|
+
<Radio
|
|
36
|
+
name={`dropS_${rUUID}`}
|
|
37
|
+
checked={fValue.cr_opt === 2}
|
|
38
|
+
onChange={() => onChange({ node, subField: sField, crOpt: 2 })}
|
|
39
|
+
>
|
|
40
|
+
Copy
|
|
41
|
+
</Radio>
|
|
42
|
+
</OverlayTrigger>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
SamOption.propTypes = {
|
|
48
|
+
sField: PropTypes.object.isRequired,
|
|
49
|
+
node: PropTypes.object.isRequired,
|
|
50
|
+
onChange: PropTypes.func.isRequired
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default SamOption;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import Select from 'react-select';
|
|
5
|
+
|
|
6
|
+
const SelectRenderer = (props) => {
|
|
7
|
+
const {
|
|
8
|
+
sField, onChange, sOptions, node
|
|
9
|
+
} = props;
|
|
10
|
+
if (sField.type !== 'select' || sOptions.length < 1) return null;
|
|
11
|
+
const { data } = node;
|
|
12
|
+
const val = sOptions.find(o => o.value === data[sField.id].value) || null;
|
|
13
|
+
return (
|
|
14
|
+
<Select
|
|
15
|
+
isClearable
|
|
16
|
+
menuContainerStyle={{ position: 'absolute' }}
|
|
17
|
+
multi={false}
|
|
18
|
+
options={sOptions}
|
|
19
|
+
value={val}
|
|
20
|
+
onChange={e => onChange(e, sField, node)}
|
|
21
|
+
className="status-select"
|
|
22
|
+
style={{ textAlign: 'left' }}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
SelectRenderer.propTypes = {
|
|
28
|
+
sField: PropTypes.object.isRequired,
|
|
29
|
+
onChange: PropTypes.func.isRequired,
|
|
30
|
+
node: PropTypes.object.isRequired,
|
|
31
|
+
sOptions: PropTypes.arrayOf(PropTypes.object).isRequired
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default SelectRenderer;
|