tentative diff highlight

This commit is contained in:
dzannotti 2015-08-07 23:52:42 +01:00
parent 61250b5562
commit fb83a18614
13 changed files with 112 additions and 22 deletions

View File

@ -14,7 +14,7 @@ export function getDefaultStyle(props) {
zIndex: 999, zIndex: 999,
fontSize: 17, fontSize: 17,
overflow: 'hidden', overflow: 'hidden',
opacity: 0.95, opacity: 0.9,
color: 'white', color: 'white',
left: left ? 0 : undefined, left: left ? 0 : undefined,
right: right ? 0 : undefined, right: right ? 0 : undefined,

View File

@ -3,6 +3,7 @@ import reactMixin from 'react-mixin';
import { ExpandedStateHandlerMixin } from './mixins'; import { ExpandedStateHandlerMixin } from './mixins';
import JSONArrow from './JSONArrow'; import JSONArrow from './JSONArrow';
import grabNode from './grab-node'; import grabNode from './grab-node';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -56,10 +57,14 @@ export default class JSONArrayNode extends React.Component {
// generated them previously, we return from cache, otherwise we create // generated them previously, we return from cache, otherwise we create
// them. // them.
getChildNodes() { getChildNodes() {
let childNodes = [];
if (this.state.expanded && this.needsChildNodes) { if (this.state.expanded && this.needsChildNodes) {
let childNodes = [];
this.props.data.forEach((element, idx) => { this.props.data.forEach((element, idx) => {
childNodes.push(grabNode(idx, element, this.props.theme)); let prevData;
if (typeof this.props.previousData !== 'undefined') {
prevData = this.props.previousData[idx];
}
childNodes.push(grabNode(idx, element, prevData, this.props.theme));
}); });
this.needsChildNodes = false; this.needsChildNodes = false;
this.renderedChildren = childNodes; this.renderedChildren = childNodes;
@ -84,11 +89,22 @@ export default class JSONArrayNode extends React.Component {
listStyle: 'none', listStyle: 'none',
display: (this.state.expanded) ? 'block' : 'none' display: (this.state.expanded) ? 'block' : 'none'
}; };
let containerStyle = { ...styles.base, ...styles.parentNode }; let backgroundColor = 'transparent';
let containerStyle;
let spanStyle = { let spanStyle = {
...styles.span, ...styles.span,
color: this.props.theme.base0E color: this.props.theme.base0E
}; };
if (typeof this.props.previousData !== 'undefined' && this.props.previousData === this.data) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
containerStyle = {
...styles.base,
...styles.parentNode,
backgroundColor
}
if (this.state.expanded) { if (this.state.expanded) {
spanStyle = { spanStyle = {
...spanStyle, ...spanStyle,

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import reactMixin from 'react-mixin'; import reactMixin from 'react-mixin';
import { SquashClickEventMixin } from './mixins'; import { SquashClickEventMixin } from './mixins';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -20,8 +21,13 @@ const styles = {
export default class JSONBooleanNode extends React.Component { export default class JSONBooleanNode extends React.Component {
render() { render() {
const truthString = (this.props.value) ? 'true' : 'false'; const truthString = (this.props.value) ? 'true' : 'false';
let backgroundColor = 'transparent';
if (this.props.previousValue !== this.props.value) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
return ( return (
<li style={styles.base} onClick={::this.handleClick}> <li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
<label style={{ <label style={{
...styles.label, ...styles.label,
color: this.props.theme.base0D color: this.props.theme.base0D

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import reactMixin from 'react-mixin'; import reactMixin from 'react-mixin';
import { SquashClickEventMixin } from './mixins'; import { SquashClickEventMixin } from './mixins';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -19,8 +20,13 @@ const styles = {
@reactMixin.decorate(SquashClickEventMixin) @reactMixin.decorate(SquashClickEventMixin)
export default class JSONNullNode extends React.Component { export default class JSONNullNode extends React.Component {
render() { render() {
let backgroundColor = 'transparent';
if (this.props.previousValue !== this.props.value) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
return ( return (
<li style={styles.base} onClick={::this.handleClick}> <li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
<label style={{ <label style={{
...styles.label, ...styles.label,
color: this.props.theme.base0D color: this.props.theme.base0D

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import reactMixin from 'react-mixin'; import reactMixin from 'react-mixin';
import { SquashClickEventMixin } from './mixins'; import { SquashClickEventMixin } from './mixins';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -19,8 +20,13 @@ const styles = {
@reactMixin.decorate(SquashClickEventMixin) @reactMixin.decorate(SquashClickEventMixin)
export default class JSONNumberNode extends React.Component { export default class JSONNumberNode extends React.Component {
render() { render() {
let backgroundColor = 'transparent';
if (this.props.previousValue !== this.props.value) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
return ( return (
<li style={styles.base} onClick={::this.handleClick}> <li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
<label style={{ <label style={{
...styles.label, ...styles.label,
color: this.props.theme.base0D color: this.props.theme.base0D

View File

@ -3,6 +3,8 @@ import reactMixin from 'react-mixin';
import { ExpandedStateHandlerMixin } from './mixins'; import { ExpandedStateHandlerMixin } from './mixins';
import JSONArrow from './JSONArrow'; import JSONArrow from './JSONArrow';
import grabNode from './grab-node'; import grabNode from './grab-node';
import shallowEqual from '../../utils/shallowEqual';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -60,7 +62,11 @@ export default class JSONObjectNode extends React.Component {
let childNodes = []; let childNodes = [];
for (let k in obj) { for (let k in obj) {
if (obj.hasOwnProperty(k)) { if (obj.hasOwnProperty(k)) {
childNodes.push(grabNode(k, obj[k], this.props.theme)); let prevData;
if (typeof this.props.previousData !== 'undefined') {
prevData = this.props.previousData[k];
}
childNodes.push(grabNode(k, obj[k], prevData, this.props.theme));
} }
} }
this.needsChildNodes = false; this.needsChildNodes = false;
@ -86,11 +92,21 @@ export default class JSONObjectNode extends React.Component {
listStyle: 'none', listStyle: 'none',
display: (this.state.expanded) ? 'block' : 'none' display: (this.state.expanded) ? 'block' : 'none'
}; };
let containerStyle = { ...styles.base, ...styles.parentNode }; let backgroundColor = 'transparent';
let containerStyle;
let spanStyle = { let spanStyle = {
...styles.span, ...styles.span,
color: this.props.theme.base0B color: this.props.theme.base0B
}; };
if (typeof this.props.previousData !== 'undefined' && !shallowEqual(this.props.data, this.props.previousData || {})) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
containerStyle = {
...styles.base,
...styles.parentNode,
backgroundColor
};
if (this.state.expanded) { if (this.state.expanded) {
spanStyle = { spanStyle = {
...spanStyle, ...spanStyle,

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import reactMixin from 'react-mixin'; import reactMixin from 'react-mixin';
import { SquashClickEventMixin } from './mixins'; import { SquashClickEventMixin } from './mixins';
import hexToRgb from '../../utils/hexToRgb';
const styles = { const styles = {
base: { base: {
@ -19,8 +20,13 @@ const styles = {
@reactMixin.decorate(SquashClickEventMixin) @reactMixin.decorate(SquashClickEventMixin)
export default class JSONStringNode extends React.Component { export default class JSONStringNode extends React.Component {
render() { render() {
let backgroundColor = 'transparent';
if (this.props.previousValue !== this.props.value) {
const bgColor = hexToRgb(this.props.theme.base08);
backgroundColor = `rgba(${bgColor.r}, ${bgColor.g}, ${bgColor.b}, 0.04)`;
}
return ( return (
<li style={styles.base} onClick={::this.handleClick}> <li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
<label style={{ <label style={{
...styles.label, ...styles.label,
color: this.props.theme.base0D color: this.props.theme.base0D

View File

@ -7,21 +7,21 @@ import JSONNumberNode from './JSONNumberNode';
import JSONBooleanNode from './JSONBooleanNode'; import JSONBooleanNode from './JSONBooleanNode';
import JSONNullNode from './JSONNullNode'; import JSONNullNode from './JSONNullNode';
export default function(key, value, theme) { export default function(key, value, prevValue, theme) {
const nodeType = objType(value); const nodeType = objType(value);
const aKey = key + Date.now(); const aKey = key + Date.now();
if (nodeType === 'Object') { if (nodeType === 'Object') {
return <JSONObjectNode data={value} theme={theme} keyName={key} key={aKey} />; return <JSONObjectNode data={value} previousData={prevValue} theme={theme} keyName={key} key={aKey} />;
} else if (nodeType === 'Array') { } else if (nodeType === 'Array') {
return <JSONArrayNode data={value} theme={theme} keyName={key} key={aKey} />; return <JSONArrayNode data={value} previousData={prevValue} theme={theme} keyName={key} key={aKey} />;
} else if (nodeType === 'String') { } else if (nodeType === 'String') {
return <JSONStringNode keyName={key} theme={theme} value={value} key={aKey} />; return <JSONStringNode keyName={key} previousValue={prevValue} theme={theme} value={value} key={aKey} />;
} else if (nodeType === 'Number') { } else if (nodeType === 'Number') {
return <JSONNumberNode keyName={key} theme={theme} value={value} key={aKey} />; return <JSONNumberNode keyName={key} previousValue={prevValue} theme={theme} value={value} key={aKey} />;
} else if (nodeType === 'Boolean') { } else if (nodeType === 'Boolean') {
return <JSONBooleanNode keyName={key} theme={theme} value={value} key={aKey} />; return <JSONBooleanNode keyName={key} previousValue={prevValue} theme={theme} value={value} key={aKey} />;
} else if (nodeType === 'Null') { } else if (nodeType === 'Null') {
return <JSONNullNode keyName={key} theme={theme} value={value} key={aKey} />; return <JSONNullNode keyName={key} previousValue={prevValue} theme={theme} value={value} key={aKey} />;
} }
console.error('Unknown node type:', nodeType); console.error('Unknown node type:', nodeType);
return false; return false;

View File

@ -38,9 +38,9 @@ export default class JSONTree extends React.Component {
let rootNode = false; let rootNode = false;
const keyName = this.props.keyName || 'root'; const keyName = this.props.keyName || 'root';
if (nodeType === 'Object') { if (nodeType === 'Object') {
rootNode = <JSONObjectNode theme={this.props.theme} data={this.props.data} keyName={keyName} initialExpanded={true} />; rootNode = <JSONObjectNode theme={this.props.theme} data={this.props.data} previousData={this.props.previousData} keyName={keyName} initialExpanded={true} />;
} else if (nodeType === 'Array') { } else if (nodeType === 'Array') {
rootNode = <JSONArrayNode theme={this.props.theme} data={this.props.data} initialExpanded={true} keyName={keyName} />; rootNode = <JSONArrayNode theme={this.props.theme} data={this.props.data} previousData={this.props.previousData} initialExpanded={true} keyName={keyName} />;
} }
return ( return (
<ul style={styles.tree}> <ul style={styles.tree}>

View File

@ -76,7 +76,6 @@ export default class LogMonitor {
if (!node) { if (!node) {
return; return;
} }
if (this.scrollDown) { if (this.scrollDown) {
const { offsetHeight, scrollHeight } = node; const { offsetHeight, scrollHeight } = node;
node.scrollTop = scrollHeight - offsetHeight; node.scrollTop = scrollHeight - offsetHeight;
@ -137,7 +136,10 @@ export default class LogMonitor {
for (let i = 0; i < stagedActions.length; i++) { for (let i = 0; i < stagedActions.length; i++) {
const action = stagedActions[i]; const action = stagedActions[i];
const { state, error } = computedStates[i]; const { state, error } = computedStates[i];
let previousState;
if (i > 0) {
previousState = computedStates[i - 1].state;
}
elements.push( elements.push(
<LogMonitorEntry key={i} <LogMonitorEntry key={i}
index={i} index={i}
@ -145,6 +147,7 @@ export default class LogMonitor {
select={select} select={select}
action={action} action={action}
state={state} state={state}
previousState={previousState}
collapsed={skippedActions[i]} collapsed={skippedActions[i]}
error={error} error={error}
onActionClick={::this.handleToggleAction} /> onActionClick={::this.handleToggleAction} />

View File

@ -32,7 +32,7 @@ export default class LogMonitorEntry {
let errorText = error; let errorText = error;
if (!errorText) { if (!errorText) {
try { try {
return <JSONTree theme={this.props.theme} keyName={'state'} data={this.props.select(state)} />; return <JSONTree theme={this.props.theme} keyName={'state'} data={this.props.select(state)} previousData={this.props.select(this.props.previousState)}/>;
} catch (err) { } catch (err) {
errorText = 'Error selecting state.'; errorText = 'Error selecting state.';
} }

8
src/utils/hexToRgb.js Normal file
View File

@ -0,0 +1,8 @@
export default function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}

23
src/utils/shallowEqual.js Normal file
View File

@ -0,0 +1,23 @@
export default function shallowEqual(objA, objB) {
if (objA === objB) {
return true;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
const hasOwn = Object.prototype.hasOwnProperty;
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
objA[keysA[i]] !== objB[keysA[i]]) {
return false;
}
}
return true;
}