added theming

This commit is contained in:
dzannotti 2015-08-07 17:36:36 +01:00
parent 50212f88a0
commit 42d4c3f890
17 changed files with 165 additions and 131 deletions

View File

@ -15,7 +15,6 @@ export function getDefaultStyle(props) {
fontSize: 17,
overflow: 'hidden',
opacity: 0.92,
background: '#181d20',
color: 'white',
left: left ? 0 : undefined,
right: right ? 0 : undefined,

View File

@ -15,17 +15,12 @@ const styles = {
label: {
margin: 0,
padding: 0,
display: 'inline-block',
color: '#8fa1b2'
display: 'inline-block'
},
span: {
color: '#C042DF',
fontSize: 12,
cursor: 'default'
},
spanExpanded: {
color: '#D7D5D8'
},
spanType: {
marginLeft: 5,
marginRight: 5,
@ -64,7 +59,7 @@ export default class JSONArrayNode extends React.Component {
let childNodes = [];
if (this.state.expanded && this.needsChildNodes) {
this.props.data.forEach((element, idx) => {
childNodes.push(grabNode(idx, element));
childNodes.push(grabNode(idx, element, this.props.theme));
});
this.needsChildNodes = false;
this.renderedChildren = childNodes;
@ -89,18 +84,26 @@ export default class JSONArrayNode extends React.Component {
listStyle: 'none',
display: (this.state.expanded) ? 'block' : 'none'
};
let containerStyle = {...styles.base, ...styles.parentNode};
let spanStyle = { ...styles.span };
let containerStyle = { ...styles.base, ...styles.parentNode };
let spanStyle = {
...styles.span,
color: this.props.theme.base0E
};
if (this.state.expanded) {
spanStyle = {
...spanStyle,
...styles.spanExpanded
color: this.props.theme.base05
};
}
return (
<li style={containerStyle} onClick={::this.handleClick}>
<JSONArrow open={this.state.expanded}/>
<label style={styles.label}>{this.props.keyName}:</label>
<JSONArrow theme={this.props.theme} open={this.state.expanded}/>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={spanStyle}>
<span style={styles.spanType}>[]</span>
{this.getItemString()}

View File

@ -11,7 +11,8 @@ const styles = {
MozTransition: '150ms',
borderLeft: '5px solid transparent',
borderRight: '5px solid transparent',
borderTop: '5px solid #39ace6',
borderTopWidth: 5,
borderTopStyle: 'solid',
WebkitTransform: 'rotateZ(-90deg)',
MozTransform: 'rotateZ(-90deg)',
transform: 'rotateZ(-90deg)'
@ -25,7 +26,10 @@ const styles = {
export default class JSONArrow extends React.Component {
render() {
let style = { ...styles.base };
let style = {
...styles.base,
borderTopColor: this.props.theme.base0C
};
if (this.props.open) {
style = {
...style,

View File

@ -12,11 +12,7 @@ const styles = {
},
label: {
display: 'inline-block',
marginRight: 5,
color: '#8fa1b2'
},
value: {
color: '#08c6eE'
marginRight: 5
}
};
@ -26,8 +22,13 @@ export default class JSONBooleanNode extends React.Component {
const truthString = (this.props.value) ? 'true' : 'false';
return (
<li style={styles.base} onClick={::this.handleClick}>
<label style={styles.label}>{this.props.keyName}:</label>
<span style={styles.value}>{truthString}</span>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={{ color: this.props.theme.base0F }}>{truthString}</span>
</li>
);
}

View File

@ -12,11 +12,7 @@ const styles = {
},
label: {
display: 'inline-block',
marginRight: 5,
color: '#8fa1b2'
},
value: {
color: '#DF113A'
marginRight: 5
}
};
@ -25,8 +21,13 @@ export default class JSONNullNode extends React.Component {
render() {
return (
<li style={styles.base} onClick={::this.handleClick}>
<label style={styles.label}>{this.props.keyName}:</label>
<span style={styles.value}>null</span>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={{ color: this.props.theme.base08 }}>null</span>
</li>
);
}

View File

@ -12,11 +12,7 @@ const styles = {
},
label: {
display: 'inline-block',
marginRight: 5,
color: '#8fa1b2'
},
value: {
color: '#0B75F5'
marginRight: 5
}
};
@ -25,8 +21,13 @@ export default class JSONNumberNode extends React.Component {
render() {
return (
<li style={styles.base} onClick={::this.handleClick}>
<label style={styles.label}>{this.props.keyName}:</label>
<span style={styles.value}>{this.props.value}</span>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={{ color: this.props.theme.base09 }}>{this.props.value}</span>
</li>
);
}

View File

@ -15,17 +15,12 @@ const styles = {
label: {
margin: 0,
padding: 0,
display: 'inline-block',
color: '#8fa1b2'
display: 'inline-block'
},
span: {
color: '#049977',
fontSize: 12,
cursor: 'default'
},
spanExpanded: {
color: '#D7D5D8'
},
spanType: {
marginLeft: 5,
marginRight: 5,
@ -65,7 +60,7 @@ export default class JSONObjectNode extends React.Component {
let childNodes = [];
for (let k in obj) {
if (obj.hasOwnProperty(k)) {
childNodes.push(grabNode(k, obj[k]));
childNodes.push(grabNode(k, obj[k], this.props.theme));
}
}
this.needsChildNodes = false;
@ -91,18 +86,26 @@ export default class JSONObjectNode extends React.Component {
listStyle: 'none',
display: (this.state.expanded) ? 'block' : 'none'
};
let containerStyle = {...styles.base, ...styles.parentNode};
let spanStyle = { ...styles.span };
let containerStyle = { ...styles.base, ...styles.parentNode };
let spanStyle = {
...styles.span,
color: this.props.theme.base0B
};
if (this.state.expanded) {
spanStyle = {
...spanStyle,
...styles.spanExpanded
color: this.props.theme.base05
};
}
return (
<li style={containerStyle} onClick={::this.handleClick}>
<JSONArrow open={this.state.expanded}/>
<label style={styles.label}>{this.props.keyName}:</label>
<JSONArrow theme={this.props.theme} open={this.state.expanded}/>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={spanStyle}>
<span style={styles.spanType}>&#123;&#125;</span>
{this.getItemString()}

View File

@ -12,11 +12,7 @@ const styles = {
},
label: {
display: 'inline-block',
marginRight: 5,
color: '#8fa1b2'
},
value: {
color: '#717c93'
marginRight: 5
}
};
@ -25,8 +21,13 @@ export default class JSONStringNode extends React.Component {
render() {
return (
<li style={styles.base} onClick={::this.handleClick}>
<label style={styles.label}>{this.props.keyName}:</label>
<span style={styles.value}>{this.props.value}</span>
<label style={{
...styles.label,
color: this.props.theme.base0D
}}>
{this.props.keyName}:
</label>
<span style={{ color: this.props.theme.base0B }}>{this.props.value}</span>
</li>
);
}

View File

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

View File

@ -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 data={this.props.data} keyName={keyName} initialExpanded={true} />;
rootNode = <JSONObjectNode theme={this.props.theme} data={this.props.data} keyName={keyName} initialExpanded={true} />;
} else if (nodeType === 'Array') {
rootNode = <JSONArrayNode data={this.props.data} initialExpanded={true} keyName={keyName} />;
rootNode = <JSONArrayNode theme={this.props.theme} data={this.props.data} initialExpanded={true} keyName={keyName} />;
}
return (
<ul style={styles.tree}>

View File

@ -1,26 +1,21 @@
import React, { PropTypes, findDOMNode } from 'react';
import LogMonitorEntry from './LogMonitorEntry';
import LogMonitorButton from './LogMonitorButton';
import themes from "./themes";
const styles = {
container: {
fontFamily: 'monospace',
position: 'relative',
overflowY: 'hidden'
overflowY: 'hidden',
width: '100%',
height: '100%'
},
buttonBarWrapper: {
backgroundColor: '#343c45',
borderBottom: '1px solid #3f464d',
marginBottom: 1
},
buttonBar: {
paddingLeft: 2
},
bordering: {
'float': 'left',
height: 1,
border: '1px solid #20262c',
width: '100%'
}
}
@ -48,6 +43,7 @@ export default class LogMonitor {
static defaultProps = {
select: (state) => state,
monitorState: { isVisible: true },
theme: 'ocean'
};
componentWillReceiveProps(nextProps) {
@ -77,7 +73,6 @@ export default class LogMonitor {
if (this.scrollDown) {
const scrollableNode = node.parentElement;
const { offsetHeight, scrollHeight } = scrollableNode;
scrollableNode.scrollTop = scrollHeight - offsetHeight;
this.scrollDown = false;
}
@ -118,6 +113,17 @@ export default class LogMonitor {
render() {
const elements = [];
const { monitorState, skippedActions, stagedActions, computedStates, select } = this.props;
let theme;
if (typeof this.props.theme === 'string') {
if (typeof themes[this.props.theme] !== 'undefined') {
theme = themes[this.props.theme]
} else {
console.warn('DevTools theme ' + this.props.theme + ' not found, defaulting to ocean');
theme = themes.ocean
}
} else {
theme = this.props.theme;
}
if (!monitorState.isVisible) {
return null;
}
@ -129,6 +135,7 @@ export default class LogMonitor {
elements.push(
<LogMonitorEntry key={i}
index={i}
theme={theme}
select={select}
action={action}
state={state}
@ -139,20 +146,22 @@ export default class LogMonitor {
}
return (
<div style={styles.container}>
<div style={styles.buttonBarWrapper}>
<div style={styles.buttonBar}>
<LogMonitorButton onClick={::this.handleReset}>Reset</LogMonitorButton>
{computedStates.length > 1 &&
<LogMonitorButton onClick={::this.handleRollback}>Revert</LogMonitorButton>
}
{Object.keys(skippedActions).some(key => skippedActions[key]) &&
<LogMonitorButton onClick={::this.handleSweep}>Sweep</LogMonitorButton>
}
{computedStates.length > 1 &&
<LogMonitorButton onClick={::this.handleCommit}>Commit</LogMonitorButton>
}
</div>
<div style={{...styles.container, backgroundColor: theme.base00}}>
<div style={{
...styles.buttonBar,
backgroundColor: theme.base01,
borderBottom: theme.base00
}}>
<LogMonitorButton theme={theme} onClick={::this.handleReset}>Reset</LogMonitorButton>
{computedStates.length > 1 &&
<LogMonitorButton theme={theme} onClick={::this.handleRollback}>Revert</LogMonitorButton>
}
{Object.keys(skippedActions).some(key => skippedActions[key]) &&
<LogMonitorButton theme={theme} onClick={::this.handleSweep}>Sweep</LogMonitorButton>
}
{computedStates.length > 1 &&
<LogMonitorButton theme={theme} onClick={::this.handleCommit}>Commit</LogMonitorButton>
}
</div>
<div styles={{
overflowX: 'hidden',

View File

@ -9,9 +9,6 @@ const styles = {
marginTop: 2,
display: 'inline-block',
fontSize: "0.8em"
},
active: {
backgroundColor: "#252c33"
}
}
@ -47,7 +44,7 @@ export default class LogMonitorButton extends React.Component {
if (this.state.hovered) {
style = {
...style,
...styles.active
backgroundColor: this.props.theme.base00
};
}
return (

View File

@ -2,38 +2,12 @@ import React, { PropTypes } from 'react';
import JSONTree from './JSONTree';
import LogMonitorEntryAction from "./LogMonitorEntryAction";
function hsvToRgb(h, s, v) {
const i = Math.floor(h);
const f = h - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
const mod = i % 6;
const r = [v, q, p, p, t, v][mod];
const g = [t, v, v, q, p, p][mod];
const b = [p, p, t, v, v, q][mod];
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
function colorFromString(token) {
function colorFromString(theme, token) {
const splitToken = token.split('');
const finalToken = splitToken.concat(splitToken.reverse());
const number = finalToken.reduce(
(sum, char) => sum + char.charCodeAt(0),
0
) * Math.abs(Math.sin(token.length));
const h = Math.round((number * (180 / Math.PI) * token.length) % 360);
const s = number % 100 / 100;
const v = 1;
return hsvToRgb(h, s, v);
const number = (parseInt(finalToken, 36) + finalToken.length) % 8;
const themeNumber = 'base0' + (number + 8).toString(16).toUpperCase();
return theme[themeNumber];
}
const styles = {
@ -58,7 +32,7 @@ export default class LogMonitorEntry {
let errorText = error;
if (!errorText) {
try {
return <JSONTree keyName={'state'} data={this.props.select(state)} />
return <JSONTree theme={this.props.theme} keyName={'state'} data={this.props.select(state)} />
} catch (err) {
errorText = 'Error selecting state.';
}
@ -82,15 +56,19 @@ export default class LogMonitorEntry {
render() {
const { index, error, action, state, collapsed } = this.props;
const { r, g, b } = colorFromString(action.type);
const styleEntry = {
opacity: collapsed ? 0.5 : 1,
color: `rgb(${r}, ${g}, ${b})`,
color: colorFromString(this.props.theme, action.type),
cursor: (index > 0) ? 'pointer' : 'default'
};
return (
<div style={{textDecoration: collapsed ? 'line-through' : 'none'}}>
<LogMonitorEntryAction collapsed={collapsed} action={action} onClick={::this.handleActionClick} style={{...styles.entry, ...styleEntry}}/>
<LogMonitorEntryAction
theme={this.props.theme}
collapsed={collapsed}
action={action}
onClick={::this.handleActionClick}
style={{...styles.entry, ...styleEntry}}/>
{!collapsed &&
<div style={{
borderBottom: '1px solid #20262c',

View File

@ -3,9 +3,10 @@ import JSONTree from './JSONTree';
const styles = {
wrapper: {
backgroundColor: '#343c45',
borderTop: '1px solid #3f464d',
borderBottom: '1px solid #3f464d'
borderTopWidth: 1,
borderBottomWidth: 1,
borderTopStyle: 'solid',
borderBottomStyle: 'solid',
},
actionBar: {
paddingTop: 4,
@ -22,14 +23,20 @@ export default class LogMonitorAction extends React.Component {
renderPayload(payload) {
return (
<div style={styles.payload}>
{ Object.keys(payload).length > 0 ? <JSONTree keyName={'payload'} data={payload}/> : "" }
{ Object.keys(payload).length > 0 ? <JSONTree theme={this.props.theme} 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.wrapper,
backgroundColor: this.props.theme.base01,
borderTopColor: this.props.theme.base00,
borderBottomColor: this.props.theme.base02,
...this.props.style
}} onClick={this.props.onClick}>
<div style={styles.actionBar}>{type}</div>
{!this.props.collapsed ? '' : ''}
</div>

5
src/react/theme.js Normal file
View File

@ -0,0 +1,5 @@
export default {
ocean: {
}
};

View File

@ -0,0 +1,5 @@
import ocean from "./ocean";
export default {
ocean,
};

20
src/react/themes/ocean.js Normal file
View File

@ -0,0 +1,20 @@
export default {
scheme: 'Ocean',
author: 'Chris Kempson (http://chriskempson.com)',
base00: '#2b303b',
base01: '#343d46',
base02: '#4f5b66',
base03: '#65737e',
base04: '#a7adba',
base05: '#c0c5ce',
base06: '#dfe1e8',
base07: '#eff1f5',
base08: '#bf616a',
base09: '#d08770',
base0A: '#ebcb8b',
base0B: '#a3be8c',
base0C: '#96b5b4',
base0D: '#8fa1b3',
base0E: '#b48ead',
base0F: '#ab7967'
}