beautified devtools look and removed lodash.assign as dep

This commit is contained in:
dzannotti 2015-08-07 16:20:13 +01:00
parent 669b1be808
commit 50212f88a0
10 changed files with 206 additions and 109 deletions

View File

@ -47,7 +47,6 @@
"redux": "^1.0.0 || 1.0.0-rc" "redux": "^1.0.0 || 1.0.0-rc"
}, },
"dependencies": { "dependencies": {
"lodash.assign": "^3.2.0",
"react-mixin": "^1.7.0", "react-mixin": "^1.7.0",
"react-redux": "^0.2.2", "react-redux": "^0.2.2",
"redux": "^1.0.0-rc" "redux": "^1.0.0-rc"

View File

@ -13,9 +13,9 @@ export function getDefaultStyle(props) {
position: 'fixed', position: 'fixed',
zIndex: 999, zIndex: 999,
fontSize: 17, fontSize: 17,
overflow: 'auto', overflow: 'hidden',
opacity: 0.92, opacity: 0.92,
background: 'black', background: '#181d20',
color: 'white', color: 'white',
left: left ? 0 : undefined, left: left ? 0 : undefined,
right: right ? 0 : undefined, right: right ? 0 : undefined,
@ -23,7 +23,9 @@ export function getDefaultStyle(props) {
bottom: bottom ? 0 : undefined, bottom: bottom ? 0 : undefined,
maxHeight: (bottom && top) ? '100%' : '20%', maxHeight: (bottom && top) ? '100%' : '20%',
maxWidth: (left && right) ? '100%' : '20%', maxWidth: (left && right) ? '100%' : '20%',
wordWrap: 'break-word' minWidth: 260,
wordWrap: 'break-word',
boxSizing: 'border-box'
}; };
} }

View File

@ -3,7 +3,6 @@ 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 assign from 'lodash.assign';
const styles = { const styles = {
base: { base: {
@ -90,8 +89,14 @@ 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 = assign({}, styles.base, styles.parentNode); let containerStyle = {...styles.base, ...styles.parentNode};
let spanStyle = assign({}, styles.span, this.state.expanded ? styles.spanExpanded : {}); let spanStyle = { ...styles.span };
if (this.state.expanded) {
spanStyle = {
...spanStyle,
...styles.spanExpanded
};
}
return ( return (
<li style={containerStyle} onClick={::this.handleClick}> <li style={containerStyle} onClick={::this.handleClick}>
<JSONArrow open={this.state.expanded}/> <JSONArrow open={this.state.expanded}/>

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import assign from 'lodash.assign';
const styles = { const styles = {
base: { base: {
@ -26,7 +25,13 @@ const styles = {
export default class JSONArrow extends React.Component { export default class JSONArrow extends React.Component {
render() { render() {
const style = assign({}, styles.base, this.props.open ? styles.open : {}); let style = { ...styles.base };
if (this.props.open) {
style = {
...style,
...styles.open
};
}
return <div style={style}/>; return <div style={style}/>;
} }
} }

View File

@ -2,7 +2,6 @@ import React from 'react';
import reactMixin from 'react-mixin'; import reactMixin from 'react-mixin';
import { ExpandedStateHandlerMixin } from './mixins'; import { ExpandedStateHandlerMixin } from './mixins';
import JSONArrow from './JSONArrow'; import JSONArrow from './JSONArrow';
import assign from 'lodash.assign';
import grabNode from './grab-node'; import grabNode from './grab-node';
const styles = { const styles = {
@ -92,8 +91,14 @@ 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 = assign({}, styles.base, styles.parentNode); let containerStyle = {...styles.base, ...styles.parentNode};
let spanStyle = assign({}, styles.span, this.state.expanded ? styles.spanExpanded : {}); let spanStyle = { ...styles.span };
if (this.state.expanded) {
spanStyle = {
...spanStyle,
...styles.spanExpanded
};
}
return ( return (
<li style={containerStyle} onClick={::this.handleClick}> <li style={containerStyle} onClick={::this.handleClick}>
<JSONArrow open={this.state.expanded}/> <JSONArrow open={this.state.expanded}/>

View File

@ -13,9 +13,9 @@ const styles = {
border: 0, border: 0,
padding: 0, padding: 0,
margin: 0, margin: 0,
fontSize: 14, fontSize: '0.90em',
listStyle: 'none', listStyle: 'none',
fontFamily: '"Helvetica Neue", Helvetica, Arial, freesans, sans-serif', fontFamily: 'monospace',
MozUserSelect: 'none', MozUserSelect: 'none',
WebkitUserSelect: 'none' WebkitUserSelect: 'none'
} }
@ -36,10 +36,11 @@ export default class JSONTree extends React.Component {
render() { render() {
const nodeType = objectType(this.props.data); const nodeType = objectType(this.props.data);
let rootNode = false; let rootNode = false;
const keyName = this.props.keyName || 'root';
if (nodeType === 'Object') { if (nodeType === 'Object') {
rootNode = <JSONObjectNode data={this.props.data} keyName="(root)" initialExpanded={true} />; rootNode = <JSONObjectNode data={this.props.data} keyName={keyName} initialExpanded={true} />;
} else if (nodeType === 'Array') { } else if (nodeType === 'Array') {
rootNode = <JSONArrayNode data={this.props.data} initialExpanded={true} keyName="(root)" />; rootNode = <JSONArrayNode data={this.props.data} initialExpanded={true} keyName={keyName} />;
} }
return ( return (
<ul style={styles.tree}> <ul style={styles.tree}>

View File

@ -1,5 +1,28 @@
import React, { PropTypes, findDOMNode } from 'react'; import React, { PropTypes, findDOMNode } from 'react';
import LogMonitorEntry from './LogMonitorEntry'; import LogMonitorEntry from './LogMonitorEntry';
import LogMonitorButton from './LogMonitorButton';
const styles = {
container: {
fontFamily: 'monospace',
position: 'relative',
overflowY: 'hidden'
},
buttonBarWrapper: {
backgroundColor: '#343c45',
borderBottom: '1px solid #3f464d',
marginBottom: 1
},
buttonBar: {
paddingLeft: 2
},
bordering: {
'float': 'left',
height: 1,
border: '1px solid #20262c',
width: '100%'
}
}
export default class LogMonitor { export default class LogMonitor {
constructor() { constructor() {
@ -24,11 +47,11 @@ export default class LogMonitor {
static defaultProps = { static defaultProps = {
select: (state) => state, select: (state) => state,
monitorState: { isVisible: true } monitorState: { isVisible: true },
}; };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const node = findDOMNode(this); const node = findDOMNode(this.refs.elements);
if (!node) { if (!node) {
this.scrollDown = true; this.scrollDown = true;
} else if ( } else if (
@ -46,7 +69,7 @@ export default class LogMonitor {
} }
componentDidUpdate() { componentDidUpdate() {
const node = findDOMNode(this); const node = findDOMNode(this.refs.elements);
if (!node) { if (!node) {
return; return;
} }
@ -95,7 +118,6 @@ export default class LogMonitor {
render() { render() {
const elements = []; const elements = [];
const { monitorState, skippedActions, stagedActions, computedStates, select } = this.props; const { monitorState, skippedActions, stagedActions, computedStates, select } = this.props;
if (!monitorState.isVisible) { if (!monitorState.isVisible) {
return null; return null;
} }
@ -117,52 +139,26 @@ export default class LogMonitor {
} }
return ( return (
<div style={{ <div style={styles.container}>
fontFamily: 'monospace', <div style={styles.buttonBarWrapper}>
position: 'relative', <div style={styles.buttonBar}>
padding: '1rem' <LogMonitorButton onClick={::this.handleReset}>Reset</LogMonitorButton>
}}> {computedStates.length > 1 &&
<div> <LogMonitorButton onClick={::this.handleRollback}>Revert</LogMonitorButton>
<div style={{ }
paddingBottom: '.5rem' {Object.keys(skippedActions).some(key => skippedActions[key]) &&
}}> <LogMonitorButton onClick={::this.handleSweep}>Sweep</LogMonitorButton>
<small>Press Ctrl+H to hide.</small> }
</div> {computedStates.length > 1 &&
<div> <LogMonitorButton onClick={::this.handleCommit}>Commit</LogMonitorButton>
<a onClick={::this.handleReset} }
style={{ textDecoration: 'underline', cursor: 'hand' }}>
<small>Reset</small>
</a>
</div> </div>
</div> </div>
{elements} <div styles={{
<div> overflowX: 'hidden',
{computedStates.length > 1 && overflowY: 'auto'
<a onClick={::this.handleRollback} }} ref="elements">
style={{ textDecoration: 'underline', cursor: 'pointer' }}> {elements}
Rollback
</a>
}
{Object.keys(skippedActions).some(key => skippedActions[key]) &&
<span>
{' • '}
<a onClick={::this.handleSweep}
style={{ textDecoration: 'underline', cursor: 'pointer' }}>
Sweep
</a>
</span>
}
{computedStates.length > 1 &&
<span>
<span>
{' • '}
</span>
<a onClick={::this.handleCommit}
style={{ textDecoration: 'underline', cursor: 'pointer' }}>
Commit
</a>
</span>
}
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,63 @@
import React from 'react';
const styles = {
base: {
paddingTop: 3,
paddingBottom: 3,
paddingLeft: 6,
paddingRight: 6,
marginTop: 2,
display: 'inline-block',
fontSize: "0.8em"
},
active: {
backgroundColor: "#252c33"
}
}
export default class LogMonitorButton extends React.Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
active: false
}
}
handleMouseEnter() {
this.setState({ hovered: true });
}
handleMouseLeave() {
this.setState({ hovered: false });
}
handleMouseDown() {
this.setState({ active: true });
}
handleMouseUp() {
this.setState({ active: false });
}
render() {
let style = {
...styles.base,
};
if (this.state.hovered) {
style = {
...style,
...styles.active
};
}
return (
<a onMouseEnter={::this.handleMouseEnter}
onMouseLeave={::this.handleMouseLeave}
onMouseDown={::this.handleMouseDown}
onMouseUp={::this.handleMouseUp}
style={style} onClick={this.props.onClick}>
{this.props.children}
</a>
);
}
}

View File

@ -1,5 +1,6 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import JSONTree from './JSONTree'; import JSONTree from './JSONTree';
import LogMonitorEntryAction from "./LogMonitorEntryAction";
function hsvToRgb(h, s, v) { function hsvToRgb(h, s, v) {
const i = Math.floor(h); const i = Math.floor(h);
@ -35,6 +36,13 @@ function colorFromString(token) {
return hsvToRgb(h, s, v); return hsvToRgb(h, s, v);
} }
const styles = {
entry: {
display: 'block',
WebkitUserSelect: 'none'
}
};
export default class LogMonitorEntry { export default class LogMonitorEntry {
static propTypes = { static propTypes = {
index: PropTypes.number.isRequired, index: PropTypes.number.isRequired,
@ -50,14 +58,14 @@ export default class LogMonitorEntry {
let errorText = error; let errorText = error;
if (!errorText) { if (!errorText) {
try { try {
return <JSONTree data={this.props.select(state)} /> return <JSONTree keyName={'state'} data={this.props.select(state)} />
} catch (err) { } catch (err) {
errorText = 'Error selecting state.'; errorText = 'Error selecting state.';
} }
} }
return ( return (
<span style={{ <span style={{
paddingLeft: 15,
fontStyle: 'italic' fontStyle: 'italic'
}}> }}>
({errorText}) ({errorText})
@ -73,49 +81,24 @@ export default class LogMonitorEntry {
} }
render() { render() {
const { index, error, action, state, collapsed } = this.props; const { index, error, action, state, collapsed } = this.props;
const { r, g, b } = colorFromString(action.type); const { r, g, b } = colorFromString(action.type);
const styleEntry = {
return ( opacity: collapsed ? 0.5 : 1,
<div style={{ color: `rgb(${r}, ${g}, ${b})`,
textDecoration: collapsed ? 'line-through' : 'none' cursor: (index > 0) ? 'pointer' : 'default'
}}> };
<a onClick={::this.handleActionClick} return (
style={{ <div style={{textDecoration: collapsed ? 'line-through' : 'none'}}>
opacity: collapsed ? 0.5 : 1, <LogMonitorEntryAction collapsed={collapsed} action={action} onClick={::this.handleActionClick} style={{...styles.entry, ...styleEntry}}/>
marginTop: '1em', {!collapsed &&
display: 'block', <div style={{
paddingBottom: '1em', borderBottom: '1px solid #20262c',
paddingTop: '1em', paddingLeft: 15
color: `rgb(${r}, ${g}, ${b})`, }}>
cursor: (index > 0) ? 'pointer' : 'default', {this.printState(state, error)}
WebkitUserSelect: 'none' </div>
}}> }
{JSON.stringify(action)}
</a>
{!collapsed &&
<p style={{
textAlign: 'center',
transform: 'rotate(180deg)'
}}>
</p>
}
{!collapsed &&
<div style={{
paddingBottom: '1em',
paddingTop: '1em',
color: 'lightyellow'
}}>
{this.printState(state, error)}
</div>
}
<hr style={{
marginBottom: '2em'
}} />
</div> </div>
); );
} }

View File

@ -0,0 +1,38 @@
import React from "react";
import JSONTree from './JSONTree';
const styles = {
wrapper: {
backgroundColor: '#343c45',
borderTop: '1px solid #3f464d',
borderBottom: '1px solid #3f464d'
},
actionBar: {
paddingTop: 4,
paddingBottom: 4,
paddingLeft: 10,
marginBottom: 1
},
payload: {
backgroundColor: '#252c33',
paddingLeft: 15
}
}
export default class LogMonitorAction extends React.Component {
renderPayload(payload) {
return (
<div style={styles.payload}>
{ Object.keys(payload).length > 0 ? <JSONTree keyName={'payload'} data={payload}/> : "" }
</div>
);
}
render() {
const { type, ...payload } = this.props.action;
return (
<div style={{...styles.wrapper, ...this.props.style}} onClick={this.props.onClick}>
<div style={styles.actionBar}>{type}</div>
{!this.props.collapsed ? '' : ''}
</div>
);
}
}