mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-15 02:54:07 +00:00
wire up run button
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user