wire up run button

This commit is contained in:
David Lechner
2020-04-17 16:57:51 -05:00
parent 232a8f5db5
commit 454475980a
16 changed files with 391 additions and 65 deletions
+5 -5
View File
@@ -4,7 +4,7 @@ import Tooltip from 'react-bootstrap/Tooltip';
import Image from 'react-bootstrap/Image';
import React from 'react';
interface ActionButtonProps {
export interface ActionButtonProps<T = undefined> {
/** A unique id for each instance. */
readonly id: string;
/** Tooltip text that appears when hovering over the button. */
@@ -14,12 +14,12 @@ interface ActionButtonProps {
/** When true or undefined, the button is enabled. */
readonly enabled?: boolean;
/** Optional action hint passed to the onAction() callback. */
readonly action?: string;
readonly context?: T;
/** Callback that is called when the button is activated (clicked). */
readonly onAction: (action?: string) => void;
readonly onAction: (context?: T) => void;
}
class ActionButton extends React.Component<ActionButtonProps> {
class ActionButton<T = undefined> extends React.Component<ActionButtonProps<T>> {
render(): JSX.Element {
return (
<OverlayTrigger
@@ -32,7 +32,7 @@ class ActionButton extends React.Component<ActionButtonProps> {
>
<Button
variant="primary"
onClick={(): void => this.props.onAction(this.props.action)}
onClick={(): void => this.props.onAction(this.props.context)}
disabled={this.props.enabled === false}
style={
this.props.enabled === false
+8 -15
View File
@@ -4,42 +4,35 @@ import { AnyAction } from 'redux';
import { connect as bleConnect, disconnect as bleDisconnect } from '../actions/ble';
import { RootState } from '../reducers';
import { BLEConnectionState } from '../reducers/ble';
import ActionButton from './ActionButton';
import ActionButton, { ActionButtonProps } from './ActionButton';
type Dispatch = ThunkDispatch<{}, {}, AnyAction>;
interface StateProps {
readonly tooltip: string;
readonly icon: string;
readonly action?: string;
readonly enabled?: boolean;
}
interface DispatchProps {
readonly onAction: (action?: string) => void;
}
type ButtonProps = ActionButtonProps<string>;
type StateProps = Pick<ButtonProps, 'tooltip' | 'icon' | 'context' | 'enabled'>;
type DispatchProps = Pick<ButtonProps, 'onAction'>;
const mapStateToProps = (state: RootState): StateProps => {
if (state.ble.connection === BLEConnectionState.Disconnected) {
return {
tooltip: 'Connect using Bluetooth',
icon: 'btdisconnected.svg',
action: 'connect',
context: 'connect',
enabled: true,
};
} else {
return {
tooltip: 'Disconnect Bluetooth',
icon: 'btconnected.svg',
action: 'disconnect',
context: 'disconnect',
enabled: state.ble.connection === BLEConnectionState.Connected,
};
}
};
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
onAction: (a): void => {
if (a === 'connect') {
onAction: (c): void => {
if (c === 'connect') {
dispatch(bleConnect());
} else {
dispatch(bleDisconnect());
+29 -16
View File
@@ -1,7 +1,9 @@
import React from 'react';
import React, { ReactElement } from 'react';
import { ReactReduxContext } from 'react-redux';
import AceEditor from 'react-ace';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import { setEditSession } from '../actions/editor';
import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/theme-xcode';
@@ -13,21 +15,32 @@ class Editor extends React.Component {
return (
<Row className="px-2 py-4 bg-primary">
<Col>
<AceEditor
mode="python"
theme="xcode"
fontSize="16pt"
width="100"
focus={true}
placeholder="Write your program here..."
defaultValue={localStorage.getItem('program') || undefined}
editorProps={{ $blockScrolling: true }}
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
}}
onChange={(v): void => localStorage.setItem('program', v)}
/>
<ReactReduxContext.Consumer>
{({ store }): ReactElement => (
<AceEditor
mode="python"
theme="xcode"
fontSize="16pt"
width="100"
focus={true}
placeholder="Write your program here..."
defaultValue={
localStorage.getItem('program') || undefined
}
editorProps={{ $blockScrolling: true }}
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
}}
onFocus={(_, e): void => {
store.dispatch(setEditSession(e?.session));
}}
onChange={(v): void =>
localStorage.setItem('program', v)
}
/>
)}
</ReactReduxContext.Consumer>
</Col>
</Row>
);
+50
View File
@@ -0,0 +1,50 @@
import { connect, batch } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { AnyAction } from 'redux';
import { Ace } from 'ace-builds';
import { RootState } from '../reducers';
import { HubRuntimeState } from '../reducers/hub';
import { compile } from '../actions/mpy';
import { downloadAndRun } from '../actions/hub';
import ActionButton, { ActionButtonProps } from './ActionButton';
type Dispatch = ThunkDispatch<{}, {}, AnyAction>;
type ButtonProps = ActionButtonProps<Ace.EditSession>;
type StateProps = Pick<ButtonProps, 'enabled' | 'context'>;
type DispatchProps = Pick<ButtonProps, 'onAction'>;
type OwnProps = Pick<ButtonProps, 'id'>;
const mapStateToProps = (state: RootState): StateProps => ({
enabled:
state.editor.current !== null && state.hub.runtime === HubRuntimeState.Idle,
context: state.editor.current || undefined,
});
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
onAction: (c): void => {
if (!c) {
console.error('No current editor');
return;
}
batch(() => {
const script = c.getValue();
const mpy = dispatch(compile(script));
dispatch(downloadAndRun(mpy.data));
});
},
});
const mergeProps = (
stateProps: StateProps,
dispatchProps: DispatchProps,
ownProps: OwnProps,
): ButtonProps => ({
tooltip: 'Download and run this program',
icon: 'run.svg',
...ownProps,
...stateProps,
...dispatchProps,
});
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(ActionButton);
+2 -6
View File
@@ -6,6 +6,7 @@ import React from 'react';
import { BLEConnectionState } from '../reducers/ble';
import ActionButton from './ActionButton';
import BluetoothButton from './BluetoothButton';
import RunButton from './RunButton';
interface ToolbarState {
bleState: BLEConnectionState;
@@ -33,12 +34,7 @@ class Toolbar extends React.Component<{}, ToolbarState> {
<ButtonToolbar className="m-2">
<ButtonGroup className="mr-2" size="lg">
<BluetoothButton id="bluetooth" />
<ActionButton
id="run"
tooltip="Download and run this program"
icon="run.svg"
onAction={this.onAction}
/>
<RunButton id="run" />
<ActionButton
id="stop"
tooltip="Stop everything"