diff --git a/openbis_ng_ui/package.json b/openbis_ng_ui/package.json index 41eb6a858624e65777bc677bf9dbeae70d4140e0..91916e3b1acb7a67703a0fa7cd6a9f04b04bbe06 100644 --- a/openbis_ng_ui/package.json +++ b/openbis_ng_ui/package.json @@ -12,12 +12,15 @@ "install": "^0.13.0", "npm": "^6.14.8", "path-to-regexp": "^6.1.0", + "prism-themes": "^1.5.0", + "prismjs": "^1.22.0", "prop-types": "^15.7.2", "re-resizable": "^6.5.4", "react": "^16.13.1", "react-beautiful-dnd": "^13.0.0", "react-dom": "^16.13.1", "react-redux": "^7.2.1", + "react-simple-code-editor": "^0.11.0", "redux": "^4.0.5", "redux-saga": "^1.1.3", "reselect": "^4.0.0", diff --git a/openbis_ng_ui/src/js/components/common/form/SourceCodeField.jsx b/openbis_ng_ui/src/js/components/common/form/SourceCodeField.jsx new file mode 100644 index 0000000000000000000000000000000000000000..dd59f4e897922046ea33f21ed12f4494d9a0f68a --- /dev/null +++ b/openbis_ng_ui/src/js/components/common/form/SourceCodeField.jsx @@ -0,0 +1,135 @@ +import _ from 'lodash' +import React from 'react' +import autoBind from 'auto-bind' +import { withStyles, withTheme } from '@material-ui/core/styles' +import { highlight, languages } from 'prismjs/components/prism-core.js' +import 'prismjs/components/prism-clike.js' +import 'prismjs/components/prism-python.js' +import 'prismjs/themes/prism.css' +import Editor from 'react-simple-code-editor' +import FormFieldContainer from '@src/js/components/common/form/FormFieldContainer.jsx' +import logger from '@src/js/common/logger.js' + +const styles = theme => ({ + view: { + fontFamily: theme.typography.sourceCode.fontFamily, + fontSize: theme.typography.body2.fontSize, + whiteSpace: 'pre-wrap', + padding: theme.spacing(2), + border: `1px solid ${theme.palette.border.secondary}` + }, + + edit: { + backgroundColor: theme.palette.background.field, + '& *': { + background: 'none !important' + }, + '& textarea': { + border: `1px solid ${theme.palette.border.primary} !important`, + borderBottom: `1px solid ${theme.palette.border.field} !important`, + outline: 'none !important' + }, + '& textarea:focus': { + borderBottom: `2px solid ${theme.palette.primary.main} !important` + } + }, + error: { + '&$edit textarea': { + borderBottom: `2px solid ${theme.palette.error.main} !important` + } + }, + disabled: { + '&$edit pre': { + opacity: 0.5 + } + } +}) + +class SourceCodeField extends React.PureComponent { + constructor(props) { + super(props) + autoBind(this) + } + + handleValueChange(value) { + const { name, onChange } = this.props + if (onChange) { + onChange({ + target: { + name, + value + } + }) + } + } + + render() { + logger.log(logger.DEBUG, 'SourceCodeField.render') + + const { mode } = this.props + + if (mode === 'view') { + return this.renderView() + } else if (mode === 'edit') { + return this.renderEdit() + } else { + throw 'Unsupported mode: ' + mode + } + } + + renderView() { + const { value, classes } = this.props + const html = { __html: highlight(value || '', languages.python) } + + return <div className={classes.view} dangerouslySetInnerHTML={html} /> + } + + renderEdit() { + const { + name, + value, + description, + disabled, + error, + onClick, + onFocus, + onBlur, + theme, + styles, + classes + } = this.props + return ( + <FormFieldContainer + description={description} + error={error} + styles={styles} + onClick={onClick} + > + <div + className={` + ${classes.edit} + ${error ? classes.error : ''} + ${disabled ? classes.disabled : ''} + `} + > + <Editor + name={name} + value={value || ''} + highlight={code => highlight(code, languages.python)} + disabled={disabled} + padding={this.props.theme.spacing(2)} + style={{ + fontFamily: theme.typography.sourceCode.fontFamily, + fontSize: this.props.theme.typography.body2.fontSize + }} + onValueChange={this.handleValueChange} + onFocus={onFocus} + onBlur={onBlur} + /> + </div> + </FormFieldContainer> + ) + } +} + +export default _.flow(withStyles(styles), withTheme)(SourceCodeField) diff --git a/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx b/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx index f67e2f45d72f35a08890e3486b9b5b7c3bc1643f..2088b09777b35f5d27fab0d066a8e3eb5af4e489 100644 --- a/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx +++ b/openbis_ng_ui/src/js/components/common/theme/ThemeProvider.jsx @@ -9,6 +9,9 @@ const config = { label: { fontSize: '0.7rem', color: '#0000008a' + }, + sourceCode: { + fontFamily: '"Fira code", "Fira Mono", monospace' } }, palette: { @@ -29,11 +32,13 @@ const config = { }, background: { primary: '#ebebeb', - secondary: '#f5f5f5' + secondary: '#f5f5f5', + field: '#e8e8e8' }, border: { primary: '#dbdbdb', - secondary: '#ebebeb' + secondary: '#ebebeb', + field: '#878787' } } } diff --git a/openbis_ng_ui/src/js/components/tools/form/plugin/PluginFormScript.jsx b/openbis_ng_ui/src/js/components/tools/form/plugin/PluginFormScript.jsx index 2536a6095ac2f7d7e157c4cdf8a782d2ca59e814..b586abcfd2d9e14d24d4fb7f164d4f3e9dd1cb3c 100644 --- a/openbis_ng_ui/src/js/components/tools/form/plugin/PluginFormScript.jsx +++ b/openbis_ng_ui/src/js/components/tools/form/plugin/PluginFormScript.jsx @@ -2,7 +2,7 @@ import React from 'react' import { withStyles } from '@material-ui/core/styles' import Container from '@src/js/components/common/form/Container.jsx' import Header from '@src/js/components/common/form/Header.jsx' -import TextField from '@src/js/components/common/form/TextField.jsx' +import SourceCodeField from '@src/js/components/common/form/SourceCodeField.jsx' import PluginFormSelectionType from '@src/js/components/tools/form/plugin/PluginFormSelectionType.js' import logger from '@src/js/common/logger.js' @@ -85,12 +85,11 @@ class PluginFormScript extends React.PureComponent { const { mode, classes } = this.props return ( <div className={classes.field}> - <TextField + <SourceCodeField reference={this.references.script} label='Script' name='script' mandatory={true} - multiline={true} error={error} disabled={!enabled} value={value}