diff --git a/src/web/widgets/Grbl/ControllerState.jsx b/src/web/widgets/Grbl/ControllerState.jsx new file mode 100644 index 000000000..60f5b6088 --- /dev/null +++ b/src/web/widgets/Grbl/ControllerState.jsx @@ -0,0 +1,51 @@ +import React, { Component, PropTypes } from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import Modal from '../../components/Modal'; +import i18n from '../../lib/i18n'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; +//import styles from './index.styl'; + +class ControllerState extends Component { + static propTypes = { + state: PropTypes.object, + actions: PropTypes.object + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + render() { + const { state, actions } = this.props; + const { modal } = state; + const show = (modal.name === MODAL_CONTROLLER_STATE); + const maxHeight = Math.max(window.innerHeight / 2, 200); + + return ( + + + {i18n._('Controller State')} + + +
{JSON.stringify(state.controller.state, null, 2)}
+
+ + + +
+ ); + } +} + +export default ControllerState; diff --git a/src/web/widgets/Grbl/Grbl.jsx b/src/web/widgets/Grbl/Grbl.jsx index cb6b1742b..476951463 100644 --- a/src/web/widgets/Grbl/Grbl.jsx +++ b/src/web/widgets/Grbl/Grbl.jsx @@ -6,6 +6,7 @@ import mapGCodeToText from '../../lib/gcode-text'; import i18n from '../../lib/i18n'; import Panel from '../../components/Panel'; import Toggler from '../../components/Toggler'; +import ControllerState from './ControllerState'; import Toolbar from './Toolbar'; import Overrides from './Overrides'; import styles from './index.styl'; @@ -46,6 +47,7 @@ class Grbl extends Component { return (
+ {!_.isEmpty(ov) && diff --git a/src/web/widgets/Grbl/Toolbar.jsx b/src/web/widgets/Grbl/Toolbar.jsx index e68b46fe0..44fb20545 100644 --- a/src/web/widgets/Grbl/Toolbar.jsx +++ b/src/web/widgets/Grbl/Toolbar.jsx @@ -1,9 +1,13 @@ +import classNames from 'classnames'; import React, { Component, PropTypes } from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import { DropdownButton, MenuItem } from 'react-bootstrap'; import i18n from '../../lib/i18n'; import controller from '../../lib/controller'; import styles from './index.styl'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; class Toolbar extends Component { static propTypes = { @@ -15,7 +19,7 @@ class Toolbar extends Component { return shallowCompare(this, nextProps, nextState); } render() { - const { state } = this.props; + const { state, actions } = this.props; const { canClick } = state; return ( @@ -24,67 +28,13 @@ class Toolbar extends Component { - -
-
- - - - -
@@ -96,12 +46,40 @@ class Toolbar extends Component { pullRight disabled={!canClick} > - controller.writeln('$')} disabled={!canClick}>{i18n._('Help ($)')} - controller.writeln('$$')} disabled={!canClick}>{i18n._('Settings ($$)')} - controller.writeln('$#')} disabled={!canClick}>{i18n._('View G-code Parameters ($#)')} - controller.writeln('$G')} disabled={!canClick}>{i18n._('View G-code Parser State ($G)')} - controller.writeln('$I')} disabled={!canClick}>{i18n._('View Build Info ($I)')} - controller.writeln('$N')} disabled={!canClick}>{i18n._('View Startup Blocks ($N)')} + controller.write('?')} disabled={!canClick}> + {i18n._('Status Report (?)')} + + controller.writeln('$C')} disabled={!canClick}> + {i18n._('Check G-code Mode ($C)')} + + controller.command('homing')} disabled={!canClick}> + {i18n._('Homing ($H)')} + + controller.command('unlock')} disabled={!canClick}> + {i18n._('Kill Alarm Lock ($X)')} + + controller.command('sleep')} disabled={!canClick}> + {i18n._('Sleep ($SLP)')} + + + controller.writeln('$')} disabled={!canClick}> + {i18n._('Help ($)')} + + controller.writeln('$$')} disabled={!canClick}> + {i18n._('Settings ($$)')} + + controller.writeln('$#')} disabled={!canClick}> + {i18n._('View G-code Parameters ($#)')} + + controller.writeln('$G')} disabled={!canClick}> + {i18n._('View G-code Parser State ($G)')} + + controller.writeln('$I')} disabled={!canClick}> + {i18n._('View Build Info ($I)')} + + controller.writeln('$N')} disabled={!canClick}> + {i18n._('View Startup Blocks ($N)')} +
diff --git a/src/web/widgets/Grbl/constants.js b/src/web/widgets/Grbl/constants.js new file mode 100644 index 000000000..8fc680327 --- /dev/null +++ b/src/web/widgets/Grbl/constants.js @@ -0,0 +1,6 @@ +import constants from 'namespace-constants'; + +module.exports = constants('widgets/Grbl', [ + 'MODAL_NONE', + 'MODAL_CONTROLLER_STATE' +]); diff --git a/src/web/widgets/Grbl/icon-status-report-16.png b/src/web/widgets/Grbl/icon-status-report-16.png new file mode 100644 index 000000000..9f2b07bdb Binary files /dev/null and b/src/web/widgets/Grbl/icon-status-report-16.png differ diff --git a/src/web/widgets/Grbl/index.jsx b/src/web/widgets/Grbl/index.jsx index 1c04a7720..26e7f14fe 100644 --- a/src/web/widgets/Grbl/index.jsx +++ b/src/web/widgets/Grbl/index.jsx @@ -10,6 +10,9 @@ import Grbl from './Grbl'; import { GRBL } from '../../constants'; +import { + MODAL_NONE +} from './constants'; import styles from './index.styl'; class GrblWidget extends Component { @@ -22,6 +25,47 @@ class GrblWidget extends Component { }; actions = { + openModal: (name = MODAL_NONE, params = {}) => { + // Prevent body from scrolling when a modal is opened + const body = document.querySelector('body'); + const bodyStyle = { + overflowY: body.style.overflowY + }; + bodyStyle.overflowY = body.style.overflowY; + body.style.overflowY = 'hidden'; + + this.setState({ + modal: { + name: name, + bodyStyle: bodyStyle, + params: params + } + }); + }, + closeModal: () => { + const body = document.querySelector('body'); + if (this.state.modal.bodyStyle) { + body.style.overflowY = this.state.modal.bodyStyle.overflowY; + } + + this.setState({ + modal: { + name: MODAL_NONE, + params: {} + } + }); + }, + updateModalParams: (params = {}) => { + this.setState({ + modal: { + ...this.state.modal, + params: { + ...this.state.modal.params, + ...params + } + } + }); + }, toggleQueueReports: () => { const expanded = this.state.panel.queueReports.expanded; @@ -118,6 +162,10 @@ class GrblWidget extends Component { type: controller.type, state: controller.state }, + modal: { + name: MODAL_NONE, + params: {} + }, panel: { queueReports: { expanded: store.get('widgets.grbl.panel.queueReports.expanded') diff --git a/src/web/widgets/Grbl/index.styl b/src/web/widgets/Grbl/index.styl index 79fb39576..d38d295ff 100644 --- a/src/web/widgets/Grbl/index.styl +++ b/src/web/widgets/Grbl/index.styl @@ -18,9 +18,18 @@ .toolbar { margin-bottom: 10px; +} + +.icon { + display: inline-block; - button { - min-width: 24px; + &.icon-status-report { + width: 16px; + height: 16px; + background-image: url("./icon-status-report-16.png"); + background-repeat: no-repeat; + vertical-align: middle; + margin-bottom: 2px; } } diff --git a/src/web/widgets/Smoothie/ControllerState.jsx b/src/web/widgets/Smoothie/ControllerState.jsx new file mode 100644 index 000000000..60f5b6088 --- /dev/null +++ b/src/web/widgets/Smoothie/ControllerState.jsx @@ -0,0 +1,51 @@ +import React, { Component, PropTypes } from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import Modal from '../../components/Modal'; +import i18n from '../../lib/i18n'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; +//import styles from './index.styl'; + +class ControllerState extends Component { + static propTypes = { + state: PropTypes.object, + actions: PropTypes.object + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + render() { + const { state, actions } = this.props; + const { modal } = state; + const show = (modal.name === MODAL_CONTROLLER_STATE); + const maxHeight = Math.max(window.innerHeight / 2, 200); + + return ( + + + {i18n._('Controller State')} + + +
{JSON.stringify(state.controller.state, null, 2)}
+
+ + + +
+ ); + } +} + +export default ControllerState; diff --git a/src/web/widgets/Smoothie/Smoothie.jsx b/src/web/widgets/Smoothie/Smoothie.jsx index 1925cf61f..af3537d0d 100644 --- a/src/web/widgets/Smoothie/Smoothie.jsx +++ b/src/web/widgets/Smoothie/Smoothie.jsx @@ -5,6 +5,7 @@ import mapGCodeToText from '../../lib/gcode-text'; import i18n from '../../lib/i18n'; import Panel from '../../components/Panel'; import Toggler from '../../components/Toggler'; +import ControllerState from './ControllerState'; import Toolbar from './Toolbar'; import Overrides from './Overrides'; import styles from './index.styl'; @@ -34,6 +35,7 @@ class Smoothie extends Component { return (
+ {(ovF !== undefined || ovS !== undefined) && diff --git a/src/web/widgets/Smoothie/Toolbar.jsx b/src/web/widgets/Smoothie/Toolbar.jsx index 882a99c8b..de07ea01f 100644 --- a/src/web/widgets/Smoothie/Toolbar.jsx +++ b/src/web/widgets/Smoothie/Toolbar.jsx @@ -1,9 +1,13 @@ +import classNames from 'classnames'; import React, { Component, PropTypes } from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import { DropdownButton, MenuItem } from 'react-bootstrap'; import i18n from '../../lib/i18n'; import controller from '../../lib/controller'; import styles from './index.styl'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; class Toolbar extends Component { static propTypes = { @@ -15,7 +19,7 @@ class Toolbar extends Component { return shallowCompare(this, nextProps, nextState); } render() { - const { state } = this.props; + const { state, actions } = this.props; const { canClick } = state; return ( @@ -24,49 +28,13 @@ class Toolbar extends Component { - -
-
- - -
@@ -78,9 +46,25 @@ class Toolbar extends Component { pullRight disabled={!canClick} > - controller.writeln('help')} disabled={!canClick}>{i18n._('Help')} - controller.writeln('$#')} disabled={!canClick}>{i18n._('View G-code Parameters ($#)')} - controller.writeln('$G')} disabled={!canClick}>{i18n._('View G-code Parser State ($G)')} + controller.write('?')} disabled={!canClick}> + {i18n._('Status Report (?)')} + + controller.command('homing')} disabled={!canClick}> + {i18n._('Homing ($H)')} + + controller.command('unlock')} disabled={!canClick}> + {i18n._('Kill Alarm Lock ($X)')} + + + controller.writeln('help')} disabled={!canClick}> + {i18n._('Help')} + + controller.writeln('$#')} disabled={!canClick}> + {i18n._('View G-code Parameters ($#)')} + + controller.writeln('$G')} disabled={!canClick}> + {i18n._('View G-code Parser State ($G)')} +
diff --git a/src/web/widgets/Smoothie/constants.js b/src/web/widgets/Smoothie/constants.js new file mode 100644 index 000000000..faec6671e --- /dev/null +++ b/src/web/widgets/Smoothie/constants.js @@ -0,0 +1,6 @@ +import constants from 'namespace-constants'; + +module.exports = constants('widgets/Smoothie', [ + 'MODAL_NONE', + 'MODAL_CONTROLLER_STATE' +]); diff --git a/src/web/widgets/Smoothie/icon-status-report-16.png b/src/web/widgets/Smoothie/icon-status-report-16.png new file mode 100644 index 000000000..9f2b07bdb Binary files /dev/null and b/src/web/widgets/Smoothie/icon-status-report-16.png differ diff --git a/src/web/widgets/Smoothie/index.jsx b/src/web/widgets/Smoothie/index.jsx index b20aa6628..0adbc1c63 100644 --- a/src/web/widgets/Smoothie/index.jsx +++ b/src/web/widgets/Smoothie/index.jsx @@ -10,6 +10,9 @@ import Smoothie from './Smoothie'; import { SMOOTHIE } from '../../constants'; +import { + MODAL_NONE +} from './constants'; import styles from './index.styl'; class SmoothieWidget extends Component { @@ -22,6 +25,47 @@ class SmoothieWidget extends Component { }; actions = { + openModal: (name = MODAL_NONE, params = {}) => { + // Prevent body from scrolling when a modal is opened + const body = document.querySelector('body'); + const bodyStyle = { + overflowY: body.style.overflowY + }; + bodyStyle.overflowY = body.style.overflowY; + body.style.overflowY = 'hidden'; + + this.setState({ + modal: { + name: name, + bodyStyle: bodyStyle, + params: params + } + }); + }, + closeModal: () => { + const body = document.querySelector('body'); + if (this.state.modal.bodyStyle) { + body.style.overflowY = this.state.modal.bodyStyle.overflowY; + } + + this.setState({ + modal: { + name: MODAL_NONE, + params: {} + } + }); + }, + updateModalParams: (params = {}) => { + this.setState({ + modal: { + ...this.state.modal, + params: { + ...this.state.modal.params, + ...params + } + } + }); + }, toggleQueueReports: () => { const expanded = this.state.panel.queueReports.expanded; @@ -118,6 +162,10 @@ class SmoothieWidget extends Component { type: controller.type, state: controller.state }, + modal: { + name: MODAL_NONE, + params: {} + }, panel: { queueReports: { expanded: store.get('widgets.smoothie.panel.queueReports.expanded') diff --git a/src/web/widgets/Smoothie/index.styl b/src/web/widgets/Smoothie/index.styl index 79fb39576..d38d295ff 100644 --- a/src/web/widgets/Smoothie/index.styl +++ b/src/web/widgets/Smoothie/index.styl @@ -18,9 +18,18 @@ .toolbar { margin-bottom: 10px; +} + +.icon { + display: inline-block; - button { - min-width: 24px; + &.icon-status-report { + width: 16px; + height: 16px; + background-image: url("./icon-status-report-16.png"); + background-repeat: no-repeat; + vertical-align: middle; + margin-bottom: 2px; } } diff --git a/src/web/widgets/TinyG/ControllerState.jsx b/src/web/widgets/TinyG/ControllerState.jsx new file mode 100644 index 000000000..60f5b6088 --- /dev/null +++ b/src/web/widgets/TinyG/ControllerState.jsx @@ -0,0 +1,51 @@ +import React, { Component, PropTypes } from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import Modal from '../../components/Modal'; +import i18n from '../../lib/i18n'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; +//import styles from './index.styl'; + +class ControllerState extends Component { + static propTypes = { + state: PropTypes.object, + actions: PropTypes.object + }; + + shouldComponentUpdate(nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + render() { + const { state, actions } = this.props; + const { modal } = state; + const show = (modal.name === MODAL_CONTROLLER_STATE); + const maxHeight = Math.max(window.innerHeight / 2, 200); + + return ( + + + {i18n._('Controller State')} + + +
{JSON.stringify(state.controller.state, null, 2)}
+
+ + + +
+ ); + } +} + +export default ControllerState; diff --git a/src/web/widgets/TinyG/TinyG.jsx b/src/web/widgets/TinyG/TinyG.jsx index 0ee698703..fc65636cb 100644 --- a/src/web/widgets/TinyG/TinyG.jsx +++ b/src/web/widgets/TinyG/TinyG.jsx @@ -6,6 +6,7 @@ import mapGCodeToText from '../../lib/gcode-text'; import i18n from '../../lib/i18n'; import Panel from '../../components/Panel'; import Toggler from '../../components/Toggler'; +import ControllerState from './ControllerState'; import Toolbar from './Toolbar'; import { TINYG_MACHINE_STATE_INITIALIZING, @@ -70,6 +71,7 @@ class TinyG extends Component { return (
+ diff --git a/src/web/widgets/TinyG/Toolbar.jsx b/src/web/widgets/TinyG/Toolbar.jsx index 3137178c4..1ae5700c4 100644 --- a/src/web/widgets/TinyG/Toolbar.jsx +++ b/src/web/widgets/TinyG/Toolbar.jsx @@ -1,9 +1,13 @@ +import classNames from 'classnames'; import React, { Component, PropTypes } from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import { DropdownButton, MenuItem } from 'react-bootstrap'; import i18n from '../../lib/i18n'; import controller from '../../lib/controller'; import styles from './index.styl'; +import { + MODAL_CONTROLLER_STATE +} from './constants'; class Toolbar extends Component { static propTypes = { @@ -15,7 +19,7 @@ class Toolbar extends Component { return shallowCompare(this, nextProps, nextState); } render() { - const { state } = this.props; + const { state, actions } = this.props; const { canClick } = state; return ( @@ -24,64 +28,13 @@ class Toolbar extends Component { - - -
-
- - -
@@ -93,14 +46,44 @@ class Toolbar extends Component { pullRight disabled={!canClick} > - controller.writeln('h')} disabled={!canClick}>{i18n._('Help')} - controller.writeln('$sys')} disabled={!canClick}>{i18n._('Show System Settings')} - controller.writeln('$$')} disabled={!canClick}>{i18n._('Show All Settings')} - controller.writeln('?')} disabled={!canClick}>{i18n._('Status Reports')} - controller.writeln('$test')} disabled={!canClick}>{i18n._('List Self Tests')} - + controller.writeln('?')} disabled={!canClick}> + {i18n._('Status Report (?)')} + + { + controller.writeln('!%'); // queue flush + controller.writeln('{"qr":""}'); // queue report + }} + disabled={!canClick} + > + {i18n._('Queue Flush (%)')} + + controller.write('\x04')} disabled={!canClick}> + {i18n._('Kill Job (^d)')} + + controller.command('unlock')} disabled={!canClick}> + {i18n._('Clear Alarm ($clear)')} + + + controller.writeln('h')} disabled={!canClick}> + {i18n._('Help')} + + controller.writeln('$sys')} disabled={!canClick}> + {i18n._('Show System Settings')} + + controller.writeln('$$')} disabled={!canClick}> + {i18n._('Show All Settings')} + + controller.writeln('?')} disabled={!canClick}> + {i18n._('Status Reports')} + + controller.writeln('$test')} disabled={!canClick}> + {i18n._('List Self Tests')} + - controller.writeln('$defa=1')} disabled={!canClick}>{i18n._('Restore Defaults')} + controller.writeln('$defa=1')} disabled={!canClick}> + {i18n._('Restore Defaults')} +
diff --git a/src/web/widgets/TinyG/constants.js b/src/web/widgets/TinyG/constants.js new file mode 100644 index 000000000..cb22ed1d5 --- /dev/null +++ b/src/web/widgets/TinyG/constants.js @@ -0,0 +1,6 @@ +import constants from 'namespace-constants'; + +module.exports = constants('widgets/TinyG', [ + 'MODAL_NONE', + 'MODAL_CONTROLLER_STATE' +]); diff --git a/src/web/widgets/TinyG/icon-status-report-16.png b/src/web/widgets/TinyG/icon-status-report-16.png new file mode 100644 index 000000000..9f2b07bdb Binary files /dev/null and b/src/web/widgets/TinyG/icon-status-report-16.png differ diff --git a/src/web/widgets/TinyG/index.jsx b/src/web/widgets/TinyG/index.jsx index ae3a727e7..a379ffffe 100644 --- a/src/web/widgets/TinyG/index.jsx +++ b/src/web/widgets/TinyG/index.jsx @@ -10,6 +10,9 @@ import TinyG from './TinyG'; import { TINYG } from '../../constants'; +import { + MODAL_NONE +} from './constants'; import styles from './index.styl'; class TinyGWidget extends Component { @@ -22,6 +25,47 @@ class TinyGWidget extends Component { }; actions = { + openModal: (name = MODAL_NONE, params = {}) => { + // Prevent body from scrolling when a modal is opened + const body = document.querySelector('body'); + const bodyStyle = { + overflowY: body.style.overflowY + }; + bodyStyle.overflowY = body.style.overflowY; + body.style.overflowY = 'hidden'; + + this.setState({ + modal: { + name: name, + bodyStyle: bodyStyle, + params: params + } + }); + }, + closeModal: () => { + const body = document.querySelector('body'); + if (this.state.modal.bodyStyle) { + body.style.overflowY = this.state.modal.bodyStyle.overflowY; + } + + this.setState({ + modal: { + name: MODAL_NONE, + params: {} + } + }); + }, + updateModalParams: (params = {}) => { + this.setState({ + modal: { + ...this.state.modal, + params: { + ...this.state.modal.params, + ...params + } + } + }); + }, toggleQueueReports: () => { const expanded = this.state.panel.queueReports.expanded; @@ -118,6 +162,10 @@ class TinyGWidget extends Component { type: controller.type, state: controller.state }, + modal: { + name: MODAL_NONE, + params: {} + }, panel: { queueReports: { expanded: store.get('widgets.tinyg.panel.queueReports.expanded') diff --git a/src/web/widgets/TinyG/index.styl b/src/web/widgets/TinyG/index.styl index 94d02218a..415478edd 100644 --- a/src/web/widgets/TinyG/index.styl +++ b/src/web/widgets/TinyG/index.styl @@ -20,9 +20,18 @@ .toolbar { margin-bottom: 10px; +} + +.icon { + display: inline-block; - button { - min-width: 24px; + &.icon-status-report { + width: 16px; + height: 16px; + background-image: url("./icon-status-report-16.png"); + background-repeat: no-repeat; + vertical-align: middle; + margin-bottom: 2px; } }