use react-dock for debug panel docking

This commit is contained in:
Kuzya 2015-09-13 04:21:49 +03:00
parent 22011114f5
commit 602fc7f59d
6 changed files with 77 additions and 87 deletions

View File

@ -47,6 +47,7 @@
"redux": "^2.0.0" "redux": "^2.0.0"
}, },
"dependencies": { "dependencies": {
"react-dock": "0.0.2",
"react-json-tree": "^0.1.8", "react-json-tree": "^0.1.8",
"react-mixin": "^1.7.0", "react-mixin": "^1.7.0",
"react-redux": "^2.0.0", "react-redux": "^2.0.0",

View File

@ -1,5 +1,6 @@
import createAll from 'react-redux/lib/components/createAll'; import createAll from 'react-redux/lib/components/createAll';
import { ActionCreators } from './devTools'; import { ActionCreators } from './devTools';
import DebugPanel from './react/DebugPanel';
export default function createDevTools(React) { export default function createDevTools(React) {
const { PropTypes, Component } = React; const { PropTypes, Component } = React;
@ -11,8 +12,13 @@ export default function createDevTools(React) {
) )
class DevTools extends Component { class DevTools extends Component {
render() { render() {
const { monitor: Monitor } = this.props; const { monitor: Monitor, panelState, setPanelState,
return <Monitor {...this.props} />; visibleOnLoad, position, ...props } = this.props;
return (
<DebugPanel {...{ panelState, setPanelState, visibleOnLoad, position }}>
<Monitor {...props} />
</DebugPanel>
);
} }
} }
@ -37,9 +43,9 @@ export default function createDevTools(React) {
} }
render() { render() {
const { store: { devToolsStore }, ...props } = this.props;
return ( return (
<DevTools {...this.props} <DevTools {...props} store={devToolsStore} />
store={this.props.store.devToolsStore} />
); );
} }
}; };

View File

@ -6,7 +6,7 @@ const ActionTypes = {
SWEEP: 'SWEEP', SWEEP: 'SWEEP',
TOGGLE_ACTION: 'TOGGLE_ACTION', TOGGLE_ACTION: 'TOGGLE_ACTION',
JUMP_TO_STATE: 'JUMP_TO_STATE', JUMP_TO_STATE: 'JUMP_TO_STATE',
SET_MONITOR_STATE: 'SET_MONITOR_STATE', SET_PANEL_STATE: 'SET_PANEL_STATE',
RECOMPUTE_STATES: 'RECOMPUTE_STATES' RECOMPUTE_STATES: 'RECOMPUTE_STATES'
}; };
@ -85,7 +85,8 @@ function liftReducer(reducer, initialState) {
stagedActions: [INIT_ACTION], stagedActions: [INIT_ACTION],
skippedActions: {}, skippedActions: {},
currentStateIndex: 0, currentStateIndex: 0,
monitorState: { panelState: {
position: 'right',
isVisible: true isVisible: true
}, },
timestamps: [Date.now()] timestamps: [Date.now()]
@ -101,7 +102,7 @@ function liftReducer(reducer, initialState) {
skippedActions, skippedActions,
computedStates, computedStates,
currentStateIndex, currentStateIndex,
monitorState, panelState,
timestamps timestamps
} = liftedState; } = liftedState;
@ -145,8 +146,8 @@ function liftReducer(reducer, initialState) {
stagedActions = [...stagedActions, liftedAction.action]; stagedActions = [...stagedActions, liftedAction.action];
timestamps = [...timestamps, liftedAction.timestamp]; timestamps = [...timestamps, liftedAction.timestamp];
break; break;
case ActionTypes.SET_MONITOR_STATE: case ActionTypes.SET_PANEL_STATE:
monitorState = liftedAction.monitorState; panelState = liftedAction.panelState;
break; break;
case ActionTypes.RECOMPUTE_STATES: case ActionTypes.RECOMPUTE_STATES:
stagedActions = liftedAction.stagedActions; stagedActions = liftedAction.stagedActions;
@ -172,7 +173,7 @@ function liftReducer(reducer, initialState) {
skippedActions, skippedActions,
computedStates, computedStates,
currentStateIndex, currentStateIndex,
monitorState, panelState,
timestamps timestamps
}; };
}; };
@ -244,8 +245,8 @@ export const ActionCreators = {
jumpToState(index) { jumpToState(index) {
return { type: ActionTypes.JUMP_TO_STATE, index }; return { type: ActionTypes.JUMP_TO_STATE, index };
}, },
setMonitorState(monitorState) { setPanelState(panelState) {
return { type: ActionTypes.SET_MONITOR_STATE, monitorState }; return { type: ActionTypes.SET_PANEL_STATE, panelState };
}, },
recomputeStates(committedState, stagedActions) { recomputeStates(committedState, stagedActions) {
return { return {

View File

@ -1,51 +1,71 @@
import React, { PropTypes, Component } from 'react'; import React, { PropTypes, Component } from 'react';
import Dock from 'react-dock';
export function getDefaultStyle(props) { const POSITIONS = ['left', 'top', 'right', 'bottom'];
let { left, right, bottom, top } = props;
if (typeof left === 'undefined' && typeof right === 'undefined') {
right = true;
}
if (typeof top === 'undefined' && typeof bottom === 'undefined') {
bottom = true;
}
return {
position: 'fixed',
zIndex: 10000,
fontSize: 17,
overflow: 'hidden',
opacity: 1,
color: 'white',
left: left ? 0 : undefined,
right: right ? 0 : undefined,
top: top ? 0 : undefined,
bottom: bottom ? 0 : undefined,
maxHeight: (bottom && top) ? '100%' : '30%',
maxWidth: (left && right) ? '100%' : '30%',
wordWrap: 'break-word',
boxSizing: 'border-box',
boxShadow: '-2px 0 7px 0 rgba(0, 0, 0, 0.5)'
};
}
export default class DebugPanel extends Component { export default class DebugPanel extends Component {
static propTypes = { static propTypes = {
left: PropTypes.bool,
right: PropTypes.bool,
bottom: PropTypes.bool,
top: PropTypes.bool,
getStyle: PropTypes.func.isRequired getStyle: PropTypes.func.isRequired
}; };
static defaultProps = { static defaultProps = {
getStyle: getDefaultStyle getStyle: () => ({}),
panelState: {
position: 'right',
isVisible: true
},
visibleOnLoad: true,
position: 'right',
dimMode: 'none'
}; };
componentWillMount() {
const { panelState, visibleOnLoad, position } = this.props;
this.props.setPanelState({
...panelState,
isVisible: visibleOnLoad,
position: position
});
}
componentDidMount() {
if (typeof window !== 'undefined') {
window.addEventListener('keydown', this.handleKeyPress);
}
}
componentWillUnmount() {
if (typeof window !== 'undefined') {
window.removeEventListener('keydown', this.handleKeyPress);
}
}
render() { render() {
const { getStyle, dimMode, panelState: { position, isVisible } } = this.props;
return ( return (
<div style={{...this.props.getStyle(this.props), ...this.props.style}}> <Dock style={{...getStyle(this.props)}}
{...{ dimMode, position, isVisible }}>
{this.props.children} {this.props.children}
</div> </Dock>
); );
} }
handleKeyPress = event => {
const { panelState } = this.props;
if (event.ctrlKey && event.keyCode === 72) { // Ctrl+H
event.preventDefault();
this.props.setPanelState({
...panelState,
isVisible: !panelState.isVisible
});
} else if (event.ctrlKey && event.keyCode === 68) { // Ctrl+D
event.preventDefault();
const positionIdx = POSITIONS.indexOf(panelState.position);
this.props.setPanelState({
...panelState,
position: POSITIONS[(positionIdx + 1) % POSITIONS.length]
});
}
}
} }

View File

@ -33,17 +33,9 @@ const styles = {
}; };
export default class LogMonitor extends Component { export default class LogMonitor extends Component {
constructor(props) {
super(props);
if (typeof window !== 'undefined') {
window.addEventListener('keydown', ::this.handleKeyPress);
}
}
static propTypes = { static propTypes = {
computedStates: PropTypes.array.isRequired, computedStates: PropTypes.array.isRequired,
currentStateIndex: PropTypes.number.isRequired, currentStateIndex: PropTypes.number.isRequired,
monitorState: PropTypes.object.isRequired,
stagedActions: PropTypes.array.isRequired, stagedActions: PropTypes.array.isRequired,
skippedActions: PropTypes.object.isRequired, skippedActions: PropTypes.object.isRequired,
reset: PropTypes.func.isRequired, reset: PropTypes.func.isRequired,
@ -52,16 +44,12 @@ export default class LogMonitor extends Component {
sweep: PropTypes.func.isRequired, sweep: PropTypes.func.isRequired,
toggleAction: PropTypes.func.isRequired, toggleAction: PropTypes.func.isRequired,
jumpToState: PropTypes.func.isRequired, jumpToState: PropTypes.func.isRequired,
setMonitorState: PropTypes.func.isRequired, select: PropTypes.func.isRequired
select: PropTypes.func.isRequired,
visibleOnLoad: PropTypes.bool
}; };
static defaultProps = { static defaultProps = {
select: (state) => state, select: (state) => state,
monitorState: { isVisible: true }, theme: 'nicinabox'
theme: 'nicinabox',
visibleOnLoad: true
}; };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
@ -93,15 +81,6 @@ export default class LogMonitor extends Component {
} }
} }
componentWillMount() {
let visibleOnLoad = this.props.visibleOnLoad;
const { monitorState } = this.props;
this.props.setMonitorState({
...monitorState,
isVisible: visibleOnLoad
});
}
handleRollback() { handleRollback() {
this.props.rollback(); this.props.rollback();
} }
@ -122,21 +101,9 @@ export default class LogMonitor extends Component {
this.props.reset(); this.props.reset();
} }
handleKeyPress(event) {
const { monitorState } = this.props;
if (event.ctrlKey && event.keyCode === 72) { // Ctrl+H
event.preventDefault();
this.props.setMonitorState({
...monitorState,
isVisible: !monitorState.isVisible
});
}
}
render() { render() {
const elements = []; const elements = [];
const { monitorState, skippedActions, stagedActions, computedStates, select } = this.props; const { skippedActions, stagedActions, computedStates, select } = this.props;
let theme; let theme;
if (typeof this.props.theme === 'string') { if (typeof this.props.theme === 'string') {
if (typeof themes[this.props.theme] !== 'undefined') { if (typeof themes[this.props.theme] !== 'undefined') {
@ -148,10 +115,6 @@ export default class LogMonitor extends Component {
} else { } else {
theme = this.props.theme; theme = this.props.theme;
} }
if (!monitorState.isVisible) {
return null;
}
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];

View File

@ -3,4 +3,3 @@ import createDevTools from '../createDevTools';
export const DevTools = createDevTools(React); export const DevTools = createDevTools(React);
export { default as LogMonitor } from './LogMonitor'; export { default as LogMonitor } from './LogMonitor';
export { default as DebugPanel } from './DebugPanel';