diff --git a/package.json b/package.json
index 31f62ba01..3296c29e9 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
"lodash.throttle": "4.0.1",
"minilog": "3.1.0",
"mkdirp": "^0.5.1",
+ "papaparse": "4.6.2",
"postcss-import": "^12.0.0",
"postcss-loader": "^3.0.0",
"postcss-simple-vars": "^5.0.1",
diff --git a/src/components/monitor/monitor.jsx b/src/components/monitor/monitor.jsx
index 7e6156ff0..221343275 100644
--- a/src/components/monitor/monitor.jsx
+++ b/src/components/monitor/monitor.jsx
@@ -54,35 +54,52 @@ const MonitorComponent = props => (
})}
- {props.mode === 'list' ? null : ReactDOM.createPortal((
+ {ReactDOM.createPortal((
// Use a portal to render the context menu outside the flow to avoid
// positioning conflicts between the monitors `transform: scale` and
// the context menus `position: fixed`. For more details, see
// http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/
-
-
- {props.onSetModeToSlider ? (
+ {props.onSetModeToDefault &&
+ }
+ {props.onSetModeToLarge &&
+ }
+ {props.onSetModeToSlider &&
- ) : null}
+ }
+ {props.onImport &&
+ }
+ {props.onExport &&
+ }
), document.body)}
@@ -100,9 +117,11 @@ MonitorComponent.propTypes = {
label: PropTypes.string.isRequired,
mode: PropTypes.oneOf(monitorModes),
onDragEnd: PropTypes.func.isRequired,
+ onExport: PropTypes.func,
+ onImport: PropTypes.func,
onNextMode: PropTypes.func.isRequired,
- onSetModeToDefault: PropTypes.func.isRequired,
- onSetModeToLarge: PropTypes.func.isRequired,
+ onSetModeToDefault: PropTypes.func,
+ onSetModeToLarge: PropTypes.func,
onSetModeToSlider: PropTypes.func
};
diff --git a/src/containers/monitor.jsx b/src/containers/monitor.jsx
index 0a02e8268..e2c882220 100644
--- a/src/containers/monitor.jsx
+++ b/src/containers/monitor.jsx
@@ -1,10 +1,14 @@
import bindAll from 'lodash.bindall';
import React from 'react';
import PropTypes from 'prop-types';
+import {injectIntl, intlShape, defineMessages} from 'react-intl';
import monitorAdapter from '../lib/monitor-adapter.js';
import MonitorComponent, {monitorModes} from '../components/monitor/monitor.jsx';
import {addMonitorRect, getInitialPosition, resizeMonitorRect, removeMonitorRect} from '../reducers/monitor-layout';
+import {getVariable, setVariableValue} from '../lib/variable-utils';
+import importCSV from '../lib/import-csv';
+import downloadText from '../lib/download-text';
import {connect} from 'react-redux';
import {Map} from 'immutable';
@@ -21,6 +25,14 @@ const availableModes = opcode => (
})
);
+const messages = defineMessages({
+ columnPrompt: {
+ defaultMessage: 'Which column should be used (1-{numberOfColumns})?',
+ description: 'Prompt for which column should be used',
+ id: 'gui.monitors.importListColumnPrompt'
+ }
+});
+
class Monitor extends React.Component {
constructor (props) {
super(props);
@@ -30,6 +42,8 @@ class Monitor extends React.Component {
'handleSetModeToDefault',
'handleSetModeToLarge',
'handleSetModeToSlider',
+ 'handleImport',
+ 'handleExport',
'setElement'
]);
}
@@ -124,9 +138,29 @@ class Monitor extends React.Component {
setElement (monitorElt) {
this.element = monitorElt;
}
+ handleImport () {
+ importCSV().then(rows => {
+ const numberOfColumns = rows[0].length;
+ let columnNumber = 1;
+ if (numberOfColumns > 1) {
+ const msg = this.props.intl.formatMessage(messages.columnPrompt, {numberOfColumns});
+ columnNumber = parseInt(prompt(msg), 10); // eslint-disable-line no-alert
+ }
+ const newListValue = rows.map(row => row[columnNumber - 1])
+ .filter(item => typeof item === 'string'); // CSV importer can leave undefineds
+ const {vm, targetId, id: variableId} = this.props;
+ setVariableValue(vm, targetId, variableId, newListValue);
+ });
+ }
+ handleExport () {
+ const {vm, targetId, id: variableId} = this.props;
+ const variable = getVariable(vm, targetId, variableId);
+ downloadText(`${variable.name}.txt`, variable.value.join('\r\n'));
+ }
render () {
const monitorProps = monitorAdapter(this.props);
const showSliderOption = availableModes(this.props.opcode).indexOf('slider') !== -1;
+ const isList = this.props.mode === 'list';
return (
);
@@ -153,6 +189,7 @@ Monitor.propTypes = {
draggable: PropTypes.bool,
height: PropTypes.number,
id: PropTypes.string.isRequired,
+ intl: intlShape,
max: PropTypes.number,
min: PropTypes.number,
mode: PropTypes.oneOf(['default', 'slider', 'large', 'list']),
@@ -190,7 +227,8 @@ const mapDispatchToProps = dispatch => ({
resizeMonitorRect: (id, newWidth, newHeight) => dispatch(resizeMonitorRect(id, newWidth, newHeight)),
removeMonitorRect: id => dispatch(removeMonitorRect(id))
});
-export default connect(
+
+export default injectIntl(connect(
mapStateToProps,
mapDispatchToProps
-)(Monitor);
+)(Monitor));
diff --git a/src/lib/download-text.js b/src/lib/download-text.js
new file mode 100644
index 000000000..2c0bc91a5
--- /dev/null
+++ b/src/lib/download-text.js
@@ -0,0 +1,13 @@
+export default (filename, text) => {
+ const pom = document.createElement('a');
+ pom.setAttribute('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`);
+ pom.setAttribute('download', filename);
+
+ if (document.createEvent) {
+ const event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ pom.dispatchEvent(event);
+ } else {
+ pom.click();
+ }
+};
diff --git a/src/lib/import-csv.js b/src/lib/import-csv.js
new file mode 100644
index 000000000..36ec7392b
--- /dev/null
+++ b/src/lib/import-csv.js
@@ -0,0 +1,23 @@
+import Papa from 'papaparse';
+
+export default () => new Promise((resolve, reject) => {
+ const fileInput = document.createElement('input');
+ fileInput.setAttribute('type', 'file');
+ fileInput.setAttribute('accept', '.csv, .tsv, .txt'); // parser auto-detects delimiter
+ fileInput.onchange = e => {
+ const file = e.target.files[0];
+ Papa.parse(file, {
+ header: false,
+ complete: results => {
+ document.body.removeChild(fileInput);
+ resolve(results.data);
+ },
+ error: err => {
+ document.body.removeChild(fileInput);
+ reject(err);
+ }
+ });
+ };
+ document.body.appendChild(fileInput);
+ fileInput.click();
+});
diff --git a/src/lib/variable-utils.js b/src/lib/variable-utils.js
index 12bcf4704..10df7df25 100644
--- a/src/lib/variable-utils.js
+++ b/src/lib/variable-utils.js
@@ -19,6 +19,7 @@ const setVariableValue = (vm, targetId, variableId, value) => {
};
export {
+ getVariable,
getVariableValue,
setVariableValue
};