diff --git a/src/components/connection-modal/connected-step.jsx b/src/components/connection-modal/connected-step.jsx new file mode 100644 index 0000000000000000000000000000000000000000..03a70235e4b68223456c72a066dec3ee004153f3 --- /dev/null +++ b/src/components/connection-modal/connected-step.jsx @@ -0,0 +1,29 @@ +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import Box from '../box/box.jsx'; + +import styles from './connection-modal.css'; + +const ConnectedStep = props => ( + <Box className={styles.body}> + <Box className={styles.buttonRow}> + <button + className={styles.searchButton} + onClick={props.onSearch} + > + <FormattedMessage + defaultMessage="connected" + description="Button in prompt for starting a search" + id="gui.connection.search" + /> + </button> + </Box> + </Box> +); + +ConnectedStep.propTypes = { +}; + +export default ConnectedStep; diff --git a/src/components/connection-modal/connecting-step.jsx b/src/components/connection-modal/connecting-step.jsx new file mode 100644 index 0000000000000000000000000000000000000000..b2b30cffbbcc65e8e4e269954cf05ad16a2ce667 --- /dev/null +++ b/src/components/connection-modal/connecting-step.jsx @@ -0,0 +1,29 @@ +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import Box from '../box/box.jsx'; + +import styles from './connection-modal.css'; + +const ConnectingStep = props => ( + <Box className={styles.body}> + <Box className={styles.buttonRow}> + <button + className={styles.searchButton} + onClick={props.onSearch} + > + <FormattedMessage + defaultMessage="connecting" + description="Button in prompt for starting a search" + id="gui.connection.search" + /> + </button> + </Box> + </Box> +); + +ConnectingStep.propTypes = { +}; + +export default ConnectingStep; diff --git a/src/components/connection-modal/connection-modal.css b/src/components/connection-modal/connection-modal.css index 34a8f5a21e3aea6ef4817aa6a2158232ca59d18d..d403af910e834fa967a664a26be26e95460cfbe9 100644 --- a/src/components/connection-modal/connection-modal.css +++ b/src/components/connection-modal/connection-modal.css @@ -5,6 +5,10 @@ width: 360px; } +.header { + background-color: $pen-primary; +} + .body { background: $ui-white; padding: 1.5rem 2.25rem; @@ -15,17 +19,6 @@ margin: 0 0 0.75rem; } -.input { - margin-bottom: 1.5rem; - width: 100%; - border: 1px solid $ui-black-transparent; - border-radius: 5px; - padding: 0 1rem; - height: 3rem; - color: $text-primary-transparent; - font-size: .875rem; -} - .button-row { font-weight: bolder; text-align: right; @@ -49,33 +42,3 @@ .button-row button + button { margin-left: 0.5rem; } - -.more-options { - border-top: 1px dashed hsla(0, 0%, 0%, .25); - overflow: visible; - padding: 1rem; - text-align: center; - margin: 0 0 1rem; -} - -.hide-more-options { - display: none; -} - -.more-options-accordion { - width: 60%; - margin: 0 auto; -} - -.more-options-text { - opacity: .5; -} - -.more-options-icon { - width: .75rem; - height: .75rem; - margin-left: .5rem; - vertical-align: middle; - padding-bottom: .2rem; - opacity: .5; -} diff --git a/src/components/connection-modal/connection-modal.jsx b/src/components/connection-modal/connection-modal.jsx index e9ce818e8121f9e5ffe02b66d4cf719916ab58d1..73699b937c319339573a1acb47577bd3a53e0d20 100644 --- a/src/components/connection-modal/connection-modal.jsx +++ b/src/components/connection-modal/connection-modal.jsx @@ -1,37 +1,40 @@ -import {FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; import React from 'react'; import Box from '../box/box.jsx'; import Modal from '../modal/modal.jsx'; +import ScanningStep from '../../containers/scanning-step.jsx'; +import ConnectingStep from './connecting-step.jsx'; +import ConnectedStep from './connected-step.jsx'; +import ErrorStep from './error-step.jsx'; + import styles from './connection-modal.css'; +const phases = { + scanning: ScanningStep, + connecting: ConnectingStep, + connected: ConnectedStep, + error: ErrorStep +}; + const ConnectionModalComponent = props => ( <Modal className={styles.modalContent} contentLabel={props.title} + headerClassName={styles.header} onRequestClose={props.onCancel} > <Box className={styles.body}> - <Box className={styles.buttonRow}> - <button - className={styles.cancelButton} - onClick={props.onCancel} - > - <FormattedMessage - defaultMessage="cancel" - description="Button in prompt for cancelling the dialog" - id="gui.prompt.cancel" - /> - </button> - </Box> + {React.createElement(phases[props.phase], props)} </Box> </Modal> ); ConnectionModalComponent.propTypes = { onCancel: PropTypes.func.isRequired, + onSearch: PropTypes.func.isRequired, + phase: PropTypes.string.isRequired, title: PropTypes.string.isRequired }; diff --git a/src/components/connection-modal/error-step.jsx b/src/components/connection-modal/error-step.jsx new file mode 100644 index 0000000000000000000000000000000000000000..451c7ab37f117caa8c28433479bb21fb215265c7 --- /dev/null +++ b/src/components/connection-modal/error-step.jsx @@ -0,0 +1,29 @@ +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import Box from '../box/box.jsx'; + +import styles from './connection-modal.css'; + +const ErrorStep = props => ( + <Box className={styles.body}> + <Box className={styles.buttonRow}> + <button + className={styles.searchButton} + onClick={props.onSearch} + > + <FormattedMessage + defaultMessage="error" + description="Button in prompt for starting a search" + id="gui.connection.search" + /> + </button> + </Box> + </Box> +); + +ErrorStep.propTypes = { +}; + +export default ErrorStep; diff --git a/src/components/connection-modal/scanning-step.jsx b/src/components/connection-modal/scanning-step.jsx new file mode 100644 index 0000000000000000000000000000000000000000..c5fc4bbe654f6ba6e8d9fa7a4c8590d87cf068e9 --- /dev/null +++ b/src/components/connection-modal/scanning-step.jsx @@ -0,0 +1,58 @@ +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import Box from '../box/box.jsx'; + +import styles from './connection-modal.css'; + +const ScanningStep = props => ( + <Box className={styles.body}> + <Box className={styles.activityArea}> + {props.searching ? ( + props.devices.length === 0 ? ( + <div>search icon here</div> + ) : ( + props.devices.map(device => ( + <div>{device.name}</div> + )) + ) + ) : ( + <div>No devices found, click on this hyperlink right now</div> + )} + </Box> + <Box className={styles.instructions}> + <FormattedMessage + defaultMessage="Select your device in the list above." + description="" + id="gui.connection.scanning.instructions" + /> + </Box> + <Box className={styles.buttonRow}> + <button + className={styles.searchButton} + onClick={props.onSearch} + > + <FormattedMessage + defaultMessage="scanning" + description="Button in prompt for starting a search" + id="gui.connection.search" + /> + </button> + </Box> + </Box> +); + +ScanningStep.propTypes = { + devices: PropTypes.arrayOf(PropTypes.shape({ + name: PropTypes.string + })), + searching: PropTypes.bool.isRequired +}; + +ScanningStep.defaultProps = { + devices: [], + searching: true +}; + +export default ScanningStep; diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index dff3ce8537b748e0c5c554d25652b8f30615efdc..631e77261c247a83f8f05109b1f7bd0b8b18f10b 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -26,7 +26,7 @@ const ModalComponent = props => ( direction="column" grow={1} > - <div className={styles.header}> + <div className={classNames(styles.header, props.headerClassName)}> <div className={classNames( styles.headerItem, @@ -74,6 +74,7 @@ ModalComponent.propTypes = { PropTypes.object ]).isRequired, fullScreen: PropTypes.bool, + headerClassName: PropTypes.string, onRequestClose: PropTypes.func }; diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 13d87d9d44268cb8d4d7b3aa0ac4a4b203929091..30c7dd9e41f85af85450d36ca5608121660da39b 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -39,6 +39,8 @@ class Blocks extends React.Component { 'attachVM', 'detachVM', 'handleCategorySelected', + 'handleConnectionModalStart', + 'handleConnectionModalClose', 'handlePromptStart', 'handlePromptCallback', 'handlePromptClose', @@ -57,12 +59,16 @@ class Blocks extends React.Component { 'setLocale' ]); this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; this.state = { workspaceMetrics: {}, - prompt: null + prompt: null, + connectionModal: null }; this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); this.toolboxUpdateQueue = []; + + this.state.connectionModal = true; } componentDidMount () { this.ScratchBlocks.FieldColourSlider.activateEyedropper_ = this.props.onActivateColorPicker; @@ -95,6 +101,7 @@ class Blocks extends React.Component { shouldComponentUpdate (nextProps, nextState) { return ( this.state.prompt !== nextState.prompt || + this.state.connectionModal !== nextState.connectionModal || this.props.isVisible !== nextProps.isVisible || this.props.toolboxXML !== nextProps.toolboxXML || this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible || @@ -327,6 +334,15 @@ class Blocks extends React.Component { optVarType !== this.ScratchBlocks.BROADCAST_MESSAGE_VARIABLE_TYPE; this.setState(p); } + handleConnectionModalStart (extensionId) { + const c = {connectionModal: { + id: extensionId + }}; + this.setState(c); + } + handleConnectionModalClose () { + this.setState({connectionModal: null}); + } handlePromptCallback (data) { this.state.prompt.callback(data); this.handlePromptClose(); @@ -366,17 +382,20 @@ class Blocks extends React.Component { {...props} /> {this.state.prompt ? ( - // <Prompt - // label={this.state.prompt.message} - // placeholder={this.state.prompt.defaultValue} - // showMoreOptions={this.state.prompt.showMoreOptions} - // title={this.state.prompt.title} - // onCancel={this.handlePromptClose} - // onOk={this.handlePromptCallback} - // /> - <ConnectionModal - title={'OMG a modal 😲 🔥 💯'} + <Prompt + label={this.state.prompt.message} + placeholder={this.state.prompt.defaultValue} + showMoreOptions={this.state.prompt.showMoreOptions} + title={this.state.prompt.title} onCancel={this.handlePromptClose} + onOk={this.handlePromptCallback} + /> + ) : null} + {this.state.connectionModal ? ( + <ConnectionModal + id={this.state.connectionModal.id} + vm={vm} + onCancel={this.handleConnectionModalClose} /> ) : null} {extensionLibraryVisible ? ( diff --git a/src/containers/connection-modal.jsx b/src/containers/connection-modal.jsx index 460db8f9680356f50169c5747099bfc514cd8202..0860b50b68ac2cd531d186d73b2f58f5bc6b4d63 100644 --- a/src/containers/connection-modal.jsx +++ b/src/containers/connection-modal.jsx @@ -7,25 +7,35 @@ class ConnectionModal extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleCancel' + 'handleCancel', + 'handleSearch' ]); + this.state = { + phase: 'scanning' + }; } handleCancel () { this.props.onCancel(); } + handleSearch () { + this.props.onSearch(); + } render () { return ( <ConnectionModalComponent - title={this.props.title} + title={this.props.id} onCancel={this.handleCancel} + onSearch={this.handleSearch} + phase={this.state.phase} /> ); } } ConnectionModal.propTypes = { + id: PropTypes.string.isRequired, onCancel: PropTypes.func.isRequired, - title: PropTypes.string.isRequired + onSearch: PropTypes.func.isRequired }; export default ConnectionModal; diff --git a/src/containers/scanning-step.jsx b/src/containers/scanning-step.jsx new file mode 100644 index 0000000000000000000000000000000000000000..44d0c578189fd21fdf8f15a24192765e20361ad3 --- /dev/null +++ b/src/containers/scanning-step.jsx @@ -0,0 +1,42 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import bindAll from 'lodash.bindall'; +import ScanningStepComponent from '../components/connection-modal/scanning-step.jsx'; + +class ScanningStep extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleCancel', + 'handleSearch' + ]); + this.state = { + searching: true, + devices: [] + }; + } + handleCancel () { + this.props.onCancel(); + } + handleSearch () { + this.props.onSearch(); + } + render () { + return ( + <ScanningStepComponent + title={this.props.id} + onCancel={this.handleCancel} + onSearch={this.handleSearch} + phase={this.state.phase} + /> + ); + } +} + +ScanningStep.propTypes = { + id: PropTypes.string.isRequired, + onCancel: PropTypes.func.isRequired, + onSearch: PropTypes.func.isRequired +}; + +export default ScanningStep; diff --git a/src/lib/libraries/extensions/index.jsx b/src/lib/libraries/extensions/index.jsx index 71404f8fb0e2abd8a47018569487c350ad581074..82b5e67d9508f306c515c3ae326a238a07c0453a 100644 --- a/src/lib/libraries/extensions/index.jsx +++ b/src/lib/libraries/extensions/index.jsx @@ -120,7 +120,7 @@ export default [ /> ), featured: true, - disabled: true + disabled: false }, { name: 'LEGO WeDo 2.0', @@ -134,7 +134,7 @@ export default [ /> ), featured: true, - disabled: true + disabled: false }, { name: 'LEGO MINDSTORMS EV3', @@ -148,7 +148,7 @@ export default [ /> ), featured: true, - disabled: true + disabled: false }, { name: 'LEGO Boost',