diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c55851a..cc99172 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -2314,6 +2314,11 @@ } } }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", @@ -4080,6 +4085,22 @@ "is-glob": "2.0.1" } }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "2.19.0", + "process": "0.5.2" + }, + "dependencies": { + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + } + } + }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -5480,6 +5501,14 @@ "mime-db": "1.33.0" } }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "0.1.1" + } + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", @@ -5625,6 +5654,11 @@ "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", "dev": true }, + "nested-property": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/nested-property/-/nested-property-0.0.7.tgz", + "integrity": "sha1-/yIvIzyoeTxoKLQRcJG+pZcTD08=" + }, "nise": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/nise/-/nise-1.2.7.tgz", @@ -8606,6 +8640,14 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "rafl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rafl/-/rafl-1.2.2.tgz", + "integrity": "sha1-/pMPdYIRAg1H44gV9Rlqi+QVB0A=", + "requires": { + "global": "4.3.2" + } + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -8750,6 +8792,15 @@ "prop-types": "15.6.1" } }, + "react-joyride": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-1.11.4.tgz", + "integrity": "sha512-312a+gLEZO6k2769rU8EcJE/MJdThuysvNjD/dgV8qxRZJHMmdaW3giRwB1X5NsaFtEXkclAEyW5/4sR32hzPw==", + "requires": { + "nested-property": "0.0.7", + "scroll": "2.0.3" + } + }, "react-overlays": { "version": "0.6.12", "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.6.12.tgz", @@ -9260,6 +9311,14 @@ "ajv": "5.5.2" } }, + "scroll": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-2.0.3.tgz", + "integrity": "sha512-3ncZzf8gUW739h3LeS68nSssO60O+GGjT3SxzgofQmT8PIoyHzebql9HHPJopZX8iT6TKOdwaWFMqL6LzUN3DQ==", + "requires": { + "rafl": "1.2.2" + } + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index b541298..f05205b 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "react-dnd": "^2.2.4", "react-dnd-html5-backend": "^2.2.4", "react-dom": "^15.1.0", + "react-joyride": "^1.11.4", "react-redux": "^4.4.5", "react-toastify": "^3.3.1", "redux": "=3.5.2", diff --git a/frontend/src/js/components/GMList.jsx b/frontend/src/js/components/GMList.jsx index 50b7a95..99655ee 100644 --- a/frontend/src/js/components/GMList.jsx +++ b/frontend/src/js/components/GMList.jsx @@ -80,7 +80,7 @@ var GMList = React.createClass({ }); return ( -
+
-

Tools

- - - - - +
+

Tools

+ Help +
+ + + + + + + + +
@@ -202,6 +209,7 @@ PlotInspector.propTypes = { undoEnabled: PropTypes.bool, redoEnabled: PropTypes.bool, show_colormap_editor: PropTypes.bool, + startTour: PropTypes.func, } export default PlotInspector; diff --git a/frontend/src/js/components/PlotInspector/PlotInspector.scss b/frontend/src/js/components/PlotInspector/PlotInspector.scss index e4a967f..01c49f5 100644 --- a/frontend/src/js/components/PlotInspector/PlotInspector.scss +++ b/frontend/src/js/components/PlotInspector/PlotInspector.scss @@ -8,8 +8,14 @@ } .tools-header{ + display: flex; + justify-content: space-between; + align-items: baseline; margin-top: 5px; margin-left: 7px; + p{ + display: inline; + } } .glyphicon.green{ diff --git a/frontend/src/js/components/TemplateList.jsx b/frontend/src/js/components/TemplateList.jsx index 85b6a06..88f5df8 100644 --- a/frontend/src/js/components/TemplateList.jsx +++ b/frontend/src/js/components/TemplateList.jsx @@ -54,7 +54,7 @@ var TemplateList = React.createClass({ render() { let template = this.state.active_template ? this.props.templates[this.state.active_template] : this.props.templates.default; return ( -
+
+
this.setState({ showFile: true, showEdit: false, selectedTab: tabs.file })} diff --git a/frontend/src/js/constants/Constants.js b/frontend/src/js/constants/Constants.js index 4469346..8a0f84c 100644 --- a/frontend/src/js/constants/Constants.js +++ b/frontend/src/js/constants/Constants.js @@ -1,5 +1,114 @@ const ONE_VAR_PLOTS = ["boxfill", "isofill", "isoline"] // meshfill can be either 1 or 2 const TWO_VAR_PLOTS = ["vector", "3d_vector", "streamline"] +const JOYRIDE_STEPS = [ + { + title: 'Welcome to vCDAT!', + text: 'The following tour will help guide you through the basic features of vCDAT. '.concat( + 'Click "Next" to continue the tour.' + ), + selector: '.joyride', + position: 'top', + type: 'click', + style: { + arrow: { + display: 'none' + }, + skip: { + color: '#f04' + }, + } + }, + { + title: 'Plot Parts', + text: 'There are three parts to any given plot. Variables, a Graphics Method, and a Template.'.concat( + ' Each of these can be dragged and dropped into a cell.' + ), + selector: '#left-side-bar', + position: 'right', + type: 'click', + style: { + hole: { + marginTop: "-1px" + } + } + }, + { + title: 'Variables', + text: 'Use the Add Edit and '.concat( + 'Delete buttons to modify what variables are loaded. ', + 'In order to edit or delete a variable, ', + 'click on it to Select it first. ', + 'Graphics Methods and Templates can be edited in the same manner.' + ), + selector: '.var-list-container', + position: 'right', + type: 'click', + }, + { + title: 'Graphics Methods', + text: 'Graphics Methods define what the plot itself should look like. '.concat( + 'Select one to edit the colormap, projection, and more.' + ), + selector: '.gm-list-container', + position: 'right', + type: 'click', + }, + { + title: 'Templates', + text: 'Templates define how the plot is presented. '.concat( + 'They control everything about the labels and axes as well as the size and location of the plots inside a cell.' + ), + selector: '.template-list-container', + position: 'right', + type: 'click', + }, + { + title: 'Spreadsheet', + text: 'The spreadsheet is made up of Cells. Each cell provides areas to drop variables, '.concat( + 'graphics methods and templates, as well as an area to drop these items to create a new plot within the cell. ', + 'Clicking on a cell will select it.' + ), + selector: '.spreadsheet-div', + position: 'top', + type: 'click', + }, + { + title: 'Spreadsheet Toolbar', + text: 'The spreadsheet toolbar controls how many rows and columns of cells should be in a given sheet. It also allows for creating and switching between sheets.', + selector: '.spreadsheet-toolbar', + position: 'right', + type: 'click', + style: { + hole: { + marginTop: "-1px" + } + } + }, + { + title: 'Inspecting Plots', + text: 'When a cell is selected, each plot inside the cell is listed here. '.concat( + 'Use this window to inspect and change things such as the variables or templates that define the plot, ', + 'as well as remove plots when there is more than one in a cell.' + ), + selector: '.plot-inspector-container', + position: 'right', + type: 'click', + }, + { + title: 'Tools', + text: 'The tools section contains various tools and actions that you may find useful. '.concat( + '
Add Plot will add an additional plot to a cell. ', + 'Use this as an overlay or as an in-cell side-by-side comparison.', + '
Clear Cell will reset the cell back to the default. ', + 'This can be undone if you accidentally click it with the undo button.', + '
Colormap Editor will open a window for creating, editing, and applying colormaps.' + ), + selector: '.tools-container', + position: 'right', + type: 'click', + }, +] +export { JOYRIDE_STEPS } export { ONE_VAR_PLOTS } export { TWO_VAR_PLOTS } diff --git a/frontend/src/js/containers/AppContainer.jsx b/frontend/src/js/containers/AppContainer.jsx index 37a9b38..f68d9f9 100644 --- a/frontend/src/js/containers/AppContainer.jsx +++ b/frontend/src/js/containers/AppContainer.jsx @@ -1,26 +1,85 @@ -import React from 'react'; +import React, { Component } from 'react'; import TopBar from './TopBar/TopBar.jsx' import LeftSideBar from './LeftSideBar.jsx'; -import RightSideBar from './RightSideBar.jsx'; import SpreadsheetContainer from './SpreadsheetContainer/SpreadsheetContainer.jsx'; import { ActionCreators as UndoActionCreators } from 'redux-undo'; import { connect } from 'react-redux'; import { DragDropContext } from 'react-dnd'; import HTML5Backend from 'react-dnd-html5-backend'; import { ToastContainer } from 'react-toastify'; +import { JOYRIDE_STEPS } from '../constants/Constants.js' +import Joyride from 'react-joyride' +import 'react-joyride/lib/react-joyride.scss' /* global jQuery */ -var AppContainer = React.createClass({ - propTypes: { - undo: React.PropTypes.func, - redo: React.PropTypes.func, - undoEnabled: React.PropTypes.bool, - redoEnabled: React.PropTypes.bool - }, +class AppContainer extends Component{ + constructor(props){ + super(props) + this.state = { + jr_steps: JOYRIDE_STEPS, + jr_run: false, + jr_auto_start: false, + } + this.startTour = this.startTour.bind(this) + this.handleJoyrideEvents = this.handleJoyrideEvents.bind(this) + } + + componentDidMount(){ + + } + + startTour(){ + if(this.joyride){ + this.setState({jr_run: true}) + } + } + + handleJoyrideEvents(event){ + console.log(event) + if(!event){ + console.log("jr event was undefined") + return + } + switch(event.type){ + case "step:after": + if(event.action !== "close"){ + return // Only continue to reset on a close action + } + // falls through. + case "overlay:click": + case "finished": + this.setState({jr_run: false},() => { + this.joyride.reset(false) // pass .reset(false) so it does not start the tour again immediately + }) + return + case "error:target_not_found": + console.warn("Joyride element missing on step ", event.index) + } + + } + render() { return (
- + {this.joyride = el}} + steps={this.state.jr_steps} + run={this.state.jr_run} + autoStart={true} + type='continuous' + showSkipButton={true} + showStepsProgress={true} + scrollToSteps={false} + scrollToFirstStep={false} + holePadding={0} + callback={this.handleJoyrideEvents} + /> +
@@ -29,7 +88,14 @@ var AppContainer = React.createClass({
); } -}); +} + +AppContainer.propTypes = { + undo: React.PropTypes.func, + redo: React.PropTypes.func, + undoEnabled: React.PropTypes.bool, + redoEnabled: React.PropTypes.bool +} const mapStateToProps = (state) => { var undoEnabled = state.past.length > 0;