2018-01-19 17:16:04 -07:00
|
|
|
// SignalLegendEntry.js
|
|
|
|
|
|
|
|
import React, { Component } from "react";
|
|
|
|
import PropTypes from "prop-types";
|
|
|
|
import cx from "classnames";
|
|
|
|
|
|
|
|
import Signal from "../../models/can/signal";
|
|
|
|
import SignalForm from "./SignalForm";
|
|
|
|
import ColorBar from "./ColorBar";
|
2018-01-22 12:27:05 -07:00
|
|
|
import FIELDS from "./FIELDS";
|
2018-01-19 17:16:04 -07:00
|
|
|
|
|
|
|
export default class SignalLegendEntry extends Component {
|
|
|
|
static propTypes = {
|
|
|
|
signal: PropTypes.instanceOf(Signal).isRequired,
|
|
|
|
isHighlighted: PropTypes.bool,
|
|
|
|
onSignalHover: PropTypes.func,
|
|
|
|
onSignalHoverEnd: PropTypes.func,
|
|
|
|
onTentativeSignalChange: PropTypes.func,
|
|
|
|
onSignalChange: PropTypes.func,
|
|
|
|
onSignalRemove: PropTypes.func,
|
|
|
|
onSignalPlotChange: PropTypes.func,
|
|
|
|
toggleExpandSignal: PropTypes.func,
|
|
|
|
isPlotted: PropTypes.bool,
|
|
|
|
isExpanded: PropTypes.bool
|
|
|
|
};
|
|
|
|
|
|
|
|
static fieldSpecForName = name => {
|
2018-01-22 12:27:05 -07:00
|
|
|
return FIELDS.find(field => field.field === name);
|
2018-01-19 17:16:04 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
signalEdited: Object.assign(Object.create(props.signal), props.signal)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
|
|
if (!nextProps.signal.equals(this.props.signal)) {
|
|
|
|
this.setState({
|
|
|
|
signalEdited: Object.assign(
|
|
|
|
Object.create(nextProps.signal),
|
|
|
|
nextProps.signal
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateField = (fieldSpec, value) => {
|
|
|
|
let { signalEdited } = this.state;
|
|
|
|
const { signal } = this.props;
|
|
|
|
|
|
|
|
if (fieldSpec.transform) {
|
|
|
|
signalEdited = fieldSpec.transform(value, signalEdited);
|
|
|
|
} else {
|
|
|
|
signalEdited[fieldSpec.field] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save entire signal while editing
|
|
|
|
this.setState({ signalEdited });
|
|
|
|
const signalCopy = Object.assign(Object.create(signal), signal);
|
|
|
|
Object.entries(signalEdited).forEach(([field, value]) => {
|
|
|
|
signalCopy[field] = value;
|
|
|
|
});
|
|
|
|
this.props.onSignalChange(signalCopy, signal);
|
|
|
|
};
|
|
|
|
|
|
|
|
toggleEditing = e => {
|
|
|
|
let { signalEdited } = this.state;
|
|
|
|
const { signal, isExpanded } = this.props;
|
|
|
|
const signalCopy = Object.assign(Object.create(signal), signal);
|
|
|
|
|
|
|
|
if (isExpanded) {
|
|
|
|
// Finished editing, save changes & reset intermediate
|
|
|
|
// signalEdited state.
|
|
|
|
Object.entries(signalEdited).forEach(([field, value]) => {
|
|
|
|
const fieldSpec = SignalLegendEntry.fieldSpecForName(field);
|
|
|
|
|
|
|
|
if (
|
|
|
|
fieldSpec &&
|
|
|
|
fieldSpec.type === "number" &&
|
|
|
|
isNaN(parseInt(value, 10))
|
|
|
|
) {
|
|
|
|
value = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
signalCopy[field] = value;
|
|
|
|
});
|
|
|
|
this.props.onSignalChange(signalCopy, signal);
|
|
|
|
} else {
|
|
|
|
signalEdited = signalCopy;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expand and enable signal editing
|
|
|
|
this.setState({
|
|
|
|
signalEdited
|
|
|
|
});
|
|
|
|
this.props.toggleExpandSignal(signal);
|
|
|
|
e.stopPropagation();
|
|
|
|
};
|
|
|
|
|
|
|
|
toggleSignalPlot = e => {
|
|
|
|
const { signal, isPlotted } = this.props;
|
|
|
|
e.preventDefault();
|
|
|
|
this.props.onSignalPlotChange(!isPlotted, signal.uid);
|
|
|
|
};
|
|
|
|
|
|
|
|
getSignalEdited = field => {
|
|
|
|
return this.state.signalEdited[field];
|
|
|
|
};
|
|
|
|
render() {
|
|
|
|
const { signal, isHighlighted, color, isPlotted, isExpanded } = this.props;
|
|
|
|
const expandedEntryClass = isExpanded ? "is-expanded" : null;
|
|
|
|
const plottedButtonClass = isPlotted ? "button" : "button--alpha";
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className={cx("signals-legend-entry", expandedEntryClass)}
|
|
|
|
onMouseEnter={() => this.props.onSignalHover(signal)}
|
|
|
|
onMouseLeave={() => this.props.onSignalHoverEnd(signal)}
|
|
|
|
>
|
|
|
|
<ColorBar isHighlighted={isHighlighted} rgb={color} />
|
|
|
|
<div className="signals-legend-entry-header">
|
|
|
|
<div
|
|
|
|
className="signals-legend-entry-header-name"
|
|
|
|
onClick={this.toggleEditing}
|
|
|
|
>
|
|
|
|
<strong>{signal.name}</strong>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
className="signals-legend-entry-header-action"
|
|
|
|
onClick={this.toggleSignalPlot}
|
|
|
|
>
|
|
|
|
<button className={cx("button--tiny", plottedButtonClass)}>
|
|
|
|
{isPlotted ? "Hide Plot" : "Show Plot"}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="signals-legend-entry-body">
|
|
|
|
{isExpanded && (
|
|
|
|
<SignalForm
|
|
|
|
signal={signal}
|
|
|
|
onSignalRemove={this.props.onSignalRemove}
|
|
|
|
isExpanded={isExpanded}
|
|
|
|
getSignalEdited={this.getSignalEdited}
|
|
|
|
update={this.updateField}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|