cabana: edit message modal; separate available / selected messages
parent
e2ac4cc236
commit
78e146d41b
|
@ -14,6 +14,7 @@ const CanFetcher = require('./workers/can-fetcher.worker.js');
|
|||
const MessageParser = require("./workers/message-parser.worker.js");
|
||||
const CanOffsetFinder = require('./workers/can-offset-finder.worker.js');
|
||||
import debounce from './utils/debounce';
|
||||
import EditMessageModal from './components/EditMessageModal';
|
||||
|
||||
export default class CanExplorer extends Component {
|
||||
static propTypes = {
|
||||
|
@ -33,6 +34,8 @@ export default class CanExplorer extends Component {
|
|||
currentParts: [0,0],
|
||||
showLoadDbc: false,
|
||||
showSaveDbc: false,
|
||||
showEditMessageModal: false,
|
||||
editMessageModalMessage: null,
|
||||
dbc: null,
|
||||
dbcFilename: null,
|
||||
dbcLastSaved: null
|
||||
|
@ -42,11 +45,13 @@ export default class CanExplorer extends Component {
|
|||
this.hideLoadDbc = this.hideLoadDbc.bind(this);
|
||||
this.showSaveDbc = this.showSaveDbc.bind(this);
|
||||
this.hideSaveDbc = this.hideSaveDbc.bind(this);
|
||||
|
||||
this.showEditMessageModal = this.showEditMessageModal.bind(this);
|
||||
this.hideEditMessageModal = this.hideEditMessageModal.bind(this);
|
||||
this.onDbcSelected = this.onDbcSelected.bind(this);
|
||||
this.onDbcSaved = this.onDbcSaved.bind(this);
|
||||
this.onConfirmedSignalChange = this.onConfirmedSignalChange.bind(this);
|
||||
this.onPartChange = this.onPartChange.bind(this);
|
||||
this.onMessageEdited = this.onMessageEdited.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
|
@ -198,6 +203,22 @@ export default class CanExplorer extends Component {
|
|||
});
|
||||
}, 500);
|
||||
|
||||
showEditMessageModal(msgKey) {
|
||||
this.setState({showEditMessageModal: true,
|
||||
editMessageModalMessage: msgKey});
|
||||
}
|
||||
|
||||
hideEditMessageModal() {
|
||||
this.setState({showEditMessageModal: false});
|
||||
}
|
||||
|
||||
onMessageEdited(messageFrame) {
|
||||
const message = this.state.messages[this.state.editMessageModalMessage];
|
||||
message.frame = messageFrame;
|
||||
this.setState({messages: this.state.messages});
|
||||
this.hideEditMessageModal();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div className={css(Styles.root)}>
|
||||
<Meta url={this.state.route.url}
|
||||
|
@ -209,7 +230,8 @@ export default class CanExplorer extends Component {
|
|||
showSaveDbc={this.showSaveDbc}
|
||||
dbcFilename={this.state.dbcFilename}
|
||||
dbcLastSaved={this.state.dbcLastSaved}
|
||||
onPartChange={this.onPartChange} />
|
||||
onPartChange={this.onPartChange}
|
||||
showEditMessageModal={this.showEditMessageModal} />
|
||||
{Object.keys(this.state.messages).length > 0
|
||||
&& this.state.selectedMessage ?
|
||||
<Explorer
|
||||
|
@ -229,6 +251,11 @@ export default class CanExplorer extends Component {
|
|||
sourceDbcFilename={this.state.dbcFilename}
|
||||
onDbcSaved={this.onDbcSaved}
|
||||
onCancel={this.hideSaveDbc} /> : null}
|
||||
{this.state.showEditMessageModal ?
|
||||
<EditMessageModal
|
||||
onCancel={this.hideEditMessageModal}
|
||||
onMessageEdited={this.onMessageEdited}
|
||||
messageFrame={this.state.messages[this.state.editMessageModalMessage].frame} /> : null}
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ export default class AddSignals extends Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps({message}) {
|
||||
const isNewMessage = message.name != this.props.message.name;
|
||||
const isNewMessage = message.address != this.props.message.address;
|
||||
|
||||
if(isNewMessage) {
|
||||
const signalStyles = this.updateSignalStyles(message.signals);
|
||||
|
|
|
@ -80,7 +80,7 @@ export default class CanHistogram extends Component {
|
|||
data={{binned: this.state.bins.bins}}
|
||||
onSignalSegment={this.onSignalSegment}
|
||||
/>
|
||||
<p className={css(Styles.label)}>{this.props.message.name} per time</p>
|
||||
<p className={css(Styles.label)}>{this.props.message.frame ? this.props.message.frame.name : this.props.message.id} per time</p>
|
||||
</div>)
|
||||
: null}
|
||||
|
||||
|
|
|
@ -54,11 +54,14 @@ export default class CanLog extends Component {
|
|||
|| JSON.stringify(nextState) != JSON.stringify(this.state)
|
||||
|| (this.props.message !== undefined
|
||||
&& nextProps.message !== undefined
|
||||
&& this.props.message.signals
|
||||
&& nextProps.message.signals
|
||||
&& !elementWiseEquals(
|
||||
Object.keys(this.props.message.signals),
|
||||
Object.keys(nextProps.message.signals)));
|
||||
&&
|
||||
(
|
||||
(this.props.message.signals
|
||||
&& nextProps.message.signals
|
||||
&& JSON.stringify(this.props.message.signals) != JSON.stringify(nextProps.message.signals))
|
||||
||
|
||||
(JSON.stringify(this.props.message.frame) != JSON.stringify(nextProps.message.frame))
|
||||
));
|
||||
return shouldUpdate;
|
||||
}
|
||||
|
||||
|
@ -143,7 +146,7 @@ export default class CanLog extends Component {
|
|||
</div>
|
||||
<div className={css(Styles.col,
|
||||
Styles.messageCol)}>
|
||||
{this.props.message.name || this.props.message.id}
|
||||
{(this.props.message.frame ? this.props.message.frame.name : null) || this.props.message.id}
|
||||
</div>
|
||||
<div className={css(Styles.col,
|
||||
Styles.messageCol,
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import React, { Component } from 'react';
|
||||
import { css, StyleSheet } from 'aphrodite/no-important';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Modal from './Modal';
|
||||
import Frame from '../models/can/frame';
|
||||
import {copyOmittingKey} from '../utils/object';
|
||||
|
||||
export default class EditMessageModal extends Component {
|
||||
static propTypes = {
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
onMessageEdited: PropTypes.func.isRequired,
|
||||
messageFrame: PropTypes.instanceOf(Frame).isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
messageFrame: Object.assign(Object.create(props.messageFrame), props.messageFrame)
|
||||
}
|
||||
this.onContinue = this.onContinue.bind(this);
|
||||
}
|
||||
|
||||
onContinue() {
|
||||
this.props.onMessageEdited(this.state.messageFrame);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<Modal title={"Edit Message " + this.state.messageFrame.id}
|
||||
continueEnabled={true}
|
||||
onCancel={this.props.onCancel}
|
||||
onContinue={this.onContinue}>
|
||||
<div>
|
||||
<div>
|
||||
<p>Name</p>
|
||||
<input type="text"
|
||||
value={this.state.messageFrame.name}
|
||||
onChange={(e) => {
|
||||
const {messageFrame} = this.state;
|
||||
messageFrame.name = e.target.value;
|
||||
this.setState({messageFrame});
|
||||
}} />
|
||||
<p>Size</p>
|
||||
<input type="number"
|
||||
value={this.state.messageFrame.size}
|
||||
onChange={(e) => {
|
||||
const {messageFrame} = this.state;
|
||||
messageFrame.size = parseInt(e.target.value);
|
||||
this.setState({messageFrame});
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>);
|
||||
}
|
||||
}
|
|
@ -351,7 +351,7 @@ export default class Explorer extends Component {
|
|||
|
||||
return <CanGraph key={messageId + '_' + signalName}
|
||||
unplot={() => {this.onSignalUnplotPressed(messageId, signalName)}}
|
||||
messageName={msg.name}
|
||||
messageName={msg.frame ? msg.frame.name : null}
|
||||
signalSpec={msg.signals[signalName]}
|
||||
onSegmentChanged={this.onSegmentChanged}
|
||||
segment={this.state.segment}
|
||||
|
|
|
@ -17,7 +17,8 @@ export default class Meta extends Component {
|
|||
showLoadDbc: PropTypes.func,
|
||||
showSaveDbc: PropTypes.func,
|
||||
dbcFilename: PropTypes.string,
|
||||
dbcLastSaved: PropTypes.object // moment.js object
|
||||
dbcLastSaved: PropTypes.object, // moment.js object,
|
||||
showEditMessageModal: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -25,7 +26,9 @@ export default class Meta extends Component {
|
|||
const {dbcLastSaved} = props;
|
||||
this.state = {
|
||||
filterText: 'Filter',
|
||||
lastSaved: dbcLastSaved !== null ? this.props.dbcLastSaved.fromNow() : null
|
||||
lastSaved: dbcLastSaved !== null ? this.props.dbcLastSaved.fromNow() : null,
|
||||
selectedMessages: [],
|
||||
hoveredMessages: []
|
||||
};
|
||||
this.onFilterChanged = this.onFilterChanged.bind(this);
|
||||
this.onFilterFocus = this.onFilterFocus.bind(this);
|
||||
|
@ -48,6 +51,13 @@ export default class Meta extends Component {
|
|||
if(nextProps.lastSaved !== this.props.lastSaved && typeof nextProps === 'object') {
|
||||
this.setState({lastSaved: nextProps.dbcLastSaved.fromNow()})
|
||||
}
|
||||
|
||||
const nextMsgKeys = Object.keys(nextProps.messages);
|
||||
if(JSON.stringify(nextMsgKeys) != JSON.stringify(Object.keys(this.props.messages))) {
|
||||
let {selectedMessages} = this.state;
|
||||
selectedMessages = selectedMessages.filter((m) => nextMsgKeys.indexOf(m) !== -1);
|
||||
this.setState({selectedMessages, hoveredMessages: []});
|
||||
}
|
||||
}
|
||||
|
||||
onFilterChanged(e) {
|
||||
|
@ -62,7 +72,8 @@ export default class Meta extends Component {
|
|||
|
||||
msgKeyFilter(key) {
|
||||
const {filterText} = this.state;
|
||||
const msgName = this.props.messages[key].name || '';
|
||||
const msg = this.props.messages[key];
|
||||
const msgName = (msg.frame ? msg.frame.name : '');
|
||||
|
||||
return (filterText == 'Filter'
|
||||
|| filterText == ''
|
||||
|
@ -75,6 +86,101 @@ export default class Meta extends Component {
|
|||
return dbcLastSaved.fromNow();
|
||||
}
|
||||
|
||||
onMessageHover(key) {
|
||||
let {hoveredMessages} = this.state;
|
||||
if(hoveredMessages.indexOf(key) !== -1) return;
|
||||
|
||||
hoveredMessages.push(key);
|
||||
this.setState({hoveredMessages});
|
||||
}
|
||||
|
||||
onMessageHoverEnd(key) {
|
||||
let {hoveredMessages} = this.state;
|
||||
hoveredMessages = hoveredMessages.filter((m) => m != key);
|
||||
this.setState({hoveredMessages});
|
||||
}
|
||||
|
||||
onMsgEditClick(key) {
|
||||
this.props.showEditMessageModal(key);
|
||||
}
|
||||
|
||||
onMsgRemoveClick(key) {
|
||||
let {selectedMessages} = this.state;
|
||||
selectedMessages = selectedMessages.filter((m) => m != key);
|
||||
this.setState({selectedMessages});
|
||||
}
|
||||
|
||||
hoverButtons(key) {
|
||||
return ([<div className={css(Styles.hoverButton, Styles.editButton)}
|
||||
onClick={() => this.onMsgEditClick(key)}>
|
||||
<p>Edit</p>
|
||||
</div>,
|
||||
<div className={css(Styles.hoverButton, Styles.removeButton)}
|
||||
onClick={() => this.onRemoveSelectedMsg(key)}>
|
||||
<p>Remove</p>
|
||||
</div>]);
|
||||
}
|
||||
|
||||
selectedMessagesList() {
|
||||
const {selectedMessages, hoveredMessages} = this.state;
|
||||
if(selectedMessages.length === 0) return null;
|
||||
|
||||
const messages = selectedMessages
|
||||
.sort()
|
||||
.map((key) => {
|
||||
const msg = this.props.messages[key];
|
||||
return <li key={key}
|
||||
className={css(Styles.message,
|
||||
Styles.selectedMessage)}
|
||||
onMouseEnter={() => this.onMessageHover(key)}
|
||||
onMouseLeave={() => this.onMessageHoverEnd(key)}>
|
||||
{msg.frame ? msg.frame.name : ''} ({key})
|
||||
{hoveredMessages.indexOf(key) !== -1 ? this.hoverButtons(key): null}
|
||||
</li>
|
||||
});
|
||||
return (<div>
|
||||
<p>Selected Messages</p>
|
||||
<ul className={css(Styles.messageList)}>
|
||||
{messages}
|
||||
</ul>
|
||||
</div>);
|
||||
}
|
||||
|
||||
onMessageSelected(key) {
|
||||
// uncomment when we support multiple messages
|
||||
// const selectedMessages = this.state.selectedMessages.filter((m) => m != key);
|
||||
const selectedMessages = [];
|
||||
selectedMessages.push(key);
|
||||
this.setState({selectedMessages});
|
||||
this.props.onMessageSelected(key);
|
||||
}
|
||||
|
||||
availableMessagesList() {
|
||||
if(Object.keys(this.props.messages).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (<div>
|
||||
<p>Available Messages</p>
|
||||
<input type="text"
|
||||
defaultValue="Filter"
|
||||
value={this.state.filterText}
|
||||
onFocus={this.onFilterFocus}
|
||||
onChange={this.onFilterChanged} />
|
||||
<ul className={css(Styles.messageList)}>
|
||||
{Object.keys(this.props.messages)
|
||||
.filter(this.msgKeyFilter)
|
||||
.sort()
|
||||
.map((key) => {
|
||||
const msg = this.props.messages[key];
|
||||
return <li onClick={() => {this.onMessageSelected(key)}}
|
||||
key={key}
|
||||
className={css(Styles.message)}>{msg.frame ? msg.frame.name : ''} ({key})</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={css(Styles.root)}>
|
||||
|
@ -115,23 +221,8 @@ export default class Meta extends Component {
|
|||
onPartChange={this.props.onPartChange}
|
||||
partsCount={this.props.partsCount}
|
||||
/>
|
||||
<div>
|
||||
<input type="text"
|
||||
defaultValue="Filter"
|
||||
value={this.state.filterText}
|
||||
onFocus={this.onFilterFocus}
|
||||
onChange={this.onFilterChanged} />
|
||||
<ul className={css(Styles.messageList)}>
|
||||
{Object.keys(this.props.messages)
|
||||
.filter(this.msgKeyFilter)
|
||||
.sort()
|
||||
.map((key) => (
|
||||
<li onClick={() => {this.props.onMessageSelected(key)}}
|
||||
key={key}
|
||||
className={css(Styles.message)}>{this.props.messages[key].name} ({key})</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{this.selectedMessagesList()}
|
||||
{this.availableMessagesList()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -164,7 +255,9 @@ const Styles = StyleSheet.create({
|
|||
backgroundColor: 'rgba(0,0,0,0.1)'
|
||||
},
|
||||
marginTop: 5,
|
||||
fontSize: 14
|
||||
fontSize: 14,
|
||||
display: 'flex',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
messageList: {
|
||||
margin: 0,
|
||||
|
@ -176,5 +269,22 @@ const Styles = StyleSheet.create({
|
|||
textDecoration: 'underline'
|
||||
},
|
||||
display: 'inline'
|
||||
},
|
||||
hoverButton: {
|
||||
height: 15,
|
||||
padding: 8,
|
||||
borderRadius: 4,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
marginLeft: 15
|
||||
},
|
||||
editButton: {
|
||||
backgroundColor: 'RGBA(105, 69, 33, 1.00)',
|
||||
color: 'RGBA(251, 253, 242, 1.00)'
|
||||
},
|
||||
removeButton: {
|
||||
backgroundColor: 'RGBA(255, 34, 59, 0.83)',
|
||||
color: 'RGBA(251, 253, 242, 1.00)'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -65,7 +65,9 @@ export default class DBC {
|
|||
const messageNames = [];
|
||||
|
||||
for(let msg of this.messages.values()) {
|
||||
messageNames.push(msg.name);
|
||||
if(msg.frame) {
|
||||
messageNames.push(msg.frame.name);
|
||||
}
|
||||
}
|
||||
|
||||
let msgNum = 1, msgName;
|
||||
|
@ -129,7 +131,7 @@ export default class DBC {
|
|||
|
||||
getMessageName(msgId) {
|
||||
const msg = this.messages.get(msgId);
|
||||
if(msg) return msg.name;
|
||||
if(msg && msg.frame) return msg.frame.name;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@ export function swapKeysAndValues(obj, f) {
|
|||
acc[obj[k]] = k;
|
||||
return acc
|
||||
},{})
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,8 +6,7 @@ import * as CanApi from '../api/can';
|
|||
const Int64LE = require('int64-buffer').Int64LE
|
||||
|
||||
function createMessageSpec(dbc, address, id, bus) {
|
||||
return {name: dbc.getMessageName(address),
|
||||
address: address,
|
||||
return {address: address,
|
||||
id: id,
|
||||
bus: bus,
|
||||
entries: [],
|
||||
|
|
Loading…
Reference in New Issue