mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-10-30 23:47:35 +03:00 
			
		
		
		
	tentative diff highlight
This commit is contained in:
		
							parent
							
								
									61250b5562
								
							
						
					
					
						commit
						fb83a18614
					
				|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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}> | ||||||
|  |  | ||||||
|  | @ -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} /> | ||||||
|  |  | ||||||
|  | @ -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
									
								
							
							
						
						
									
										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