mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-22 01:26:48 +03:00
tentative diff highlight
This commit is contained in:
parent
61250b5562
commit
fb83a18614
|
@ -14,7 +14,7 @@ export function getDefaultStyle(props) {
|
|||
zIndex: 999,
|
||||
fontSize: 17,
|
||||
overflow: 'hidden',
|
||||
opacity: 0.95,
|
||||
opacity: 0.9,
|
||||
color: 'white',
|
||||
left: left ? 0 : undefined,
|
||||
right: right ? 0 : undefined,
|
||||
|
|
|
@ -3,6 +3,7 @@ import reactMixin from 'react-mixin';
|
|||
import { ExpandedStateHandlerMixin } from './mixins';
|
||||
import JSONArrow from './JSONArrow';
|
||||
import grabNode from './grab-node';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -56,10 +57,14 @@ export default class JSONArrayNode extends React.Component {
|
|||
// generated them previously, we return from cache, otherwise we create
|
||||
// them.
|
||||
getChildNodes() {
|
||||
let childNodes = [];
|
||||
if (this.state.expanded && this.needsChildNodes) {
|
||||
let childNodes = [];
|
||||
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.renderedChildren = childNodes;
|
||||
|
@ -84,11 +89,22 @@ export default class JSONArrayNode extends React.Component {
|
|||
listStyle: 'none',
|
||||
display: (this.state.expanded) ? 'block' : 'none'
|
||||
};
|
||||
let containerStyle = { ...styles.base, ...styles.parentNode };
|
||||
let backgroundColor = 'transparent';
|
||||
let containerStyle;
|
||||
let spanStyle = {
|
||||
...styles.span,
|
||||
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) {
|
||||
spanStyle = {
|
||||
...spanStyle,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import reactMixin from 'react-mixin';
|
||||
import { SquashClickEventMixin } from './mixins';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -20,8 +21,13 @@ const styles = {
|
|||
export default class JSONBooleanNode extends React.Component {
|
||||
render() {
|
||||
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 (
|
||||
<li style={styles.base} onClick={::this.handleClick}>
|
||||
<li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
|
||||
<label style={{
|
||||
...styles.label,
|
||||
color: this.props.theme.base0D
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import reactMixin from 'react-mixin';
|
||||
import { SquashClickEventMixin } from './mixins';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -19,8 +20,13 @@ const styles = {
|
|||
@reactMixin.decorate(SquashClickEventMixin)
|
||||
export default class JSONNullNode extends React.Component {
|
||||
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 (
|
||||
<li style={styles.base} onClick={::this.handleClick}>
|
||||
<li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
|
||||
<label style={{
|
||||
...styles.label,
|
||||
color: this.props.theme.base0D
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import reactMixin from 'react-mixin';
|
||||
import { SquashClickEventMixin } from './mixins';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -19,8 +20,13 @@ const styles = {
|
|||
@reactMixin.decorate(SquashClickEventMixin)
|
||||
export default class JSONNumberNode extends React.Component {
|
||||
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 (
|
||||
<li style={styles.base} onClick={::this.handleClick}>
|
||||
<li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
|
||||
<label style={{
|
||||
...styles.label,
|
||||
color: this.props.theme.base0D
|
||||
|
|
|
@ -3,6 +3,8 @@ import reactMixin from 'react-mixin';
|
|||
import { ExpandedStateHandlerMixin } from './mixins';
|
||||
import JSONArrow from './JSONArrow';
|
||||
import grabNode from './grab-node';
|
||||
import shallowEqual from '../../utils/shallowEqual';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -60,7 +62,11 @@ export default class JSONObjectNode extends React.Component {
|
|||
let childNodes = [];
|
||||
for (let k in obj) {
|
||||
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;
|
||||
|
@ -86,11 +92,21 @@ export default class JSONObjectNode extends React.Component {
|
|||
listStyle: 'none',
|
||||
display: (this.state.expanded) ? 'block' : 'none'
|
||||
};
|
||||
let containerStyle = { ...styles.base, ...styles.parentNode };
|
||||
let backgroundColor = 'transparent';
|
||||
let containerStyle;
|
||||
let spanStyle = {
|
||||
...styles.span,
|
||||
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) {
|
||||
spanStyle = {
|
||||
...spanStyle,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import reactMixin from 'react-mixin';
|
||||
import { SquashClickEventMixin } from './mixins';
|
||||
import hexToRgb from '../../utils/hexToRgb';
|
||||
|
||||
const styles = {
|
||||
base: {
|
||||
|
@ -19,8 +20,13 @@ const styles = {
|
|||
@reactMixin.decorate(SquashClickEventMixin)
|
||||
export default class JSONStringNode extends React.Component {
|
||||
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 (
|
||||
<li style={styles.base} onClick={::this.handleClick}>
|
||||
<li style={{ ...styles.base, backgroundColor }} onClick={::this.handleClick}>
|
||||
<label style={{
|
||||
...styles.label,
|
||||
color: this.props.theme.base0D
|
||||
|
|
|
@ -7,21 +7,21 @@ import JSONNumberNode from './JSONNumberNode';
|
|||
import JSONBooleanNode from './JSONBooleanNode';
|
||||
import JSONNullNode from './JSONNullNode';
|
||||
|
||||
export default function(key, value, theme) {
|
||||
export default function(key, value, prevValue, theme) {
|
||||
const nodeType = objType(value);
|
||||
const aKey = key + Date.now();
|
||||
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') {
|
||||
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') {
|
||||
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') {
|
||||
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') {
|
||||
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') {
|
||||
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);
|
||||
return false;
|
||||
|
|
|
@ -38,9 +38,9 @@ export default class JSONTree extends React.Component {
|
|||
let rootNode = false;
|
||||
const keyName = this.props.keyName || 'root';
|
||||
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') {
|
||||
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 (
|
||||
<ul style={styles.tree}>
|
||||
|
|
|
@ -76,7 +76,6 @@ export default class LogMonitor {
|
|||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.scrollDown) {
|
||||
const { offsetHeight, scrollHeight } = node;
|
||||
node.scrollTop = scrollHeight - offsetHeight;
|
||||
|
@ -137,7 +136,10 @@ export default class LogMonitor {
|
|||
for (let i = 0; i < stagedActions.length; i++) {
|
||||
const action = stagedActions[i];
|
||||
const { state, error } = computedStates[i];
|
||||
|
||||
let previousState;
|
||||
if (i > 0) {
|
||||
previousState = computedStates[i - 1].state;
|
||||
}
|
||||
elements.push(
|
||||
<LogMonitorEntry key={i}
|
||||
index={i}
|
||||
|
@ -145,6 +147,7 @@ export default class LogMonitor {
|
|||
select={select}
|
||||
action={action}
|
||||
state={state}
|
||||
previousState={previousState}
|
||||
collapsed={skippedActions[i]}
|
||||
error={error}
|
||||
onActionClick={::this.handleToggleAction} />
|
||||
|
|
|
@ -32,7 +32,7 @@ export default class LogMonitorEntry {
|
|||
let errorText = error;
|
||||
if (!errorText) {
|
||||
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) {
|
||||
errorText = 'Error selecting state.';
|
||||
}
|
||||
|
|
8
src/utils/hexToRgb.js
Normal file
8
src/utils/hexToRgb.js
Normal 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
23
src/utils/shallowEqual.js
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user