mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-01-31 19:54:35 +03:00
Import the initial devTools implementation
This commit is contained in:
parent
de43b2e110
commit
644cd6fab6
|
@ -9,9 +9,10 @@
|
||||||
"react/jsx-uses-react": 2,
|
"react/jsx-uses-react": 2,
|
||||||
"react/jsx-uses-vars": 2,
|
"react/jsx-uses-vars": 2,
|
||||||
"react/react-in-jsx-scope": 2,
|
"react/react-in-jsx-scope": 2,
|
||||||
|
"no-console": 0,
|
||||||
// Temporarily disabled due to babel-eslint issues:
|
// Temporarily disabled due to babel-eslint issues:
|
||||||
"block-scoped-var": 0,
|
"block-scoped-var": 0,
|
||||||
"padded-blocks": 0
|
"padded-blocks": 0,
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"react"
|
"react"
|
||||||
|
|
55
src/createDevTools.js
Normal file
55
src/createDevTools.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import createAll from 'react-redux/lib/components/createAll';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { ActionCreators } from './devTools';
|
||||||
|
|
||||||
|
export default function createDevTools(React) {
|
||||||
|
const { PropTypes, Component } = React;
|
||||||
|
const { Provider, Connector } = createAll(React);
|
||||||
|
|
||||||
|
return class DevTools extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
monitor: PropTypes.func.isRequired,
|
||||||
|
store: PropTypes.shape({
|
||||||
|
devToolsStore: PropTypes.shape({
|
||||||
|
dispatch: PropTypes.func.isRequired
|
||||||
|
}).isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
if (props.store && !props.store.devToolsStore) {
|
||||||
|
console.error(
|
||||||
|
'Could not find the devTools store inside your store. ' +
|
||||||
|
'Have you applied devTools() higher-order store?'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
super(props, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { devToolsStore } = this.props.store;
|
||||||
|
return (
|
||||||
|
<Provider store={devToolsStore}>
|
||||||
|
{this.renderRoot}
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRoot = () => {
|
||||||
|
return (
|
||||||
|
<Connector>
|
||||||
|
{this.renderMonitor}
|
||||||
|
</Connector>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderMonitor = ({ dispatch, ...state }) => {
|
||||||
|
const { monitor: Monitor, ...rest } = this.props;
|
||||||
|
return (
|
||||||
|
<Monitor {...state}
|
||||||
|
{...bindActionCreators(ActionCreators, dispatch)}
|
||||||
|
{...rest} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
213
src/devTools.js
Normal file
213
src/devTools.js
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
const ActionTypes = {
|
||||||
|
PERFORM_ACTION: 'PERFORM_ACTION',
|
||||||
|
RESET: 'RESET',
|
||||||
|
ROLLBACK: 'ROLLBACK',
|
||||||
|
COMMIT: 'COMMIT',
|
||||||
|
SWEEP: 'SWEEP',
|
||||||
|
TOGGLE_ACTION: 'TOGGLE_ACTION'
|
||||||
|
};
|
||||||
|
|
||||||
|
const INIT_ACTION = {
|
||||||
|
type: '@@INIT'
|
||||||
|
};
|
||||||
|
|
||||||
|
function last(arr) {
|
||||||
|
return arr[arr.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle(obj, key) {
|
||||||
|
const clone = { ...obj };
|
||||||
|
if (clone[key]) {
|
||||||
|
delete clone[key];
|
||||||
|
} else {
|
||||||
|
clone[key] = true;
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the next entry in the log by applying an action.
|
||||||
|
*/
|
||||||
|
function computeNextEntry(reducer, action, state, error) {
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
error: 'Interrupted by an error up the chain'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextState = state;
|
||||||
|
let nextError;
|
||||||
|
try {
|
||||||
|
nextState = reducer(state, action);
|
||||||
|
} catch (err) {
|
||||||
|
nextError = err.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
state: nextState,
|
||||||
|
error: nextError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the reducer on all actions to get a fresh computation log.
|
||||||
|
* It's probably a good idea to do this only if the code has changed,
|
||||||
|
* but until we have some tests we'll just do it every time an action fires.
|
||||||
|
*/
|
||||||
|
function recomputeStates(reducer, committedState, stagedActions, skippedActions) {
|
||||||
|
const computedStates = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < stagedActions.length; i++) {
|
||||||
|
const action = stagedActions[i];
|
||||||
|
|
||||||
|
const previousEntry = computedStates[i - 1];
|
||||||
|
const previousState = previousEntry ? previousEntry.state : committedState;
|
||||||
|
const previousError = previousEntry ? previousEntry.error : undefined;
|
||||||
|
|
||||||
|
const shouldSkip = Boolean(skippedActions[i]);
|
||||||
|
const entry = shouldSkip ?
|
||||||
|
previousEntry :
|
||||||
|
computeNextEntry(reducer, action, previousState, previousError);
|
||||||
|
|
||||||
|
computedStates.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return computedStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifts the app state reducer into a DevTools state reducer.
|
||||||
|
*/
|
||||||
|
function liftReducer(reducer, initialState) {
|
||||||
|
const initialLiftedState = {
|
||||||
|
committedState: initialState,
|
||||||
|
stagedActions: [INIT_ACTION],
|
||||||
|
skippedActions: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages how the DevTools actions modify the DevTools state.
|
||||||
|
*/
|
||||||
|
return function liftedReducer(liftedState = initialLiftedState, liftedAction) {
|
||||||
|
let {
|
||||||
|
committedState,
|
||||||
|
stagedActions,
|
||||||
|
skippedActions,
|
||||||
|
computedStates
|
||||||
|
} = liftedState;
|
||||||
|
|
||||||
|
switch (liftedAction.type) {
|
||||||
|
case ActionTypes.RESET:
|
||||||
|
committedState = initialState;
|
||||||
|
stagedActions = [INIT_ACTION];
|
||||||
|
skippedActions = {};
|
||||||
|
break;
|
||||||
|
case ActionTypes.COMMIT:
|
||||||
|
committedState = last(computedStates).state;
|
||||||
|
stagedActions = [INIT_ACTION];
|
||||||
|
skippedActions = {};
|
||||||
|
break;
|
||||||
|
case ActionTypes.ROLLBACK:
|
||||||
|
stagedActions = [INIT_ACTION];
|
||||||
|
skippedActions = {};
|
||||||
|
break;
|
||||||
|
case ActionTypes.TOGGLE_ACTION:
|
||||||
|
const { index } = liftedAction;
|
||||||
|
skippedActions = toggle(skippedActions, index);
|
||||||
|
break;
|
||||||
|
case ActionTypes.SWEEP:
|
||||||
|
stagedActions = stagedActions.filter((_, i) => !skippedActions[i]);
|
||||||
|
skippedActions = {};
|
||||||
|
break;
|
||||||
|
case ActionTypes.PERFORM_ACTION:
|
||||||
|
const { action } = liftedAction;
|
||||||
|
stagedActions = [...stagedActions, action];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
computedStates = recomputeStates(
|
||||||
|
reducer,
|
||||||
|
committedState,
|
||||||
|
stagedActions,
|
||||||
|
skippedActions
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
committedState,
|
||||||
|
stagedActions,
|
||||||
|
skippedActions,
|
||||||
|
computedStates
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifts an app action to a DevTools action.
|
||||||
|
*/
|
||||||
|
function liftAction(action) {
|
||||||
|
const liftedAction = { type: ActionTypes.PERFORM_ACTION, action };
|
||||||
|
return liftedAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlifts the DevTools state to the app state.
|
||||||
|
*/
|
||||||
|
function unliftState(liftedState) {
|
||||||
|
const { computedStates } = liftedState;
|
||||||
|
const { state } = last(computedStates);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlifts the DevTools store to act like the app's store.
|
||||||
|
*/
|
||||||
|
function unliftStore(liftedStore) {
|
||||||
|
return {
|
||||||
|
...liftedStore,
|
||||||
|
devToolsStore: liftedStore,
|
||||||
|
dispatch(action) {
|
||||||
|
liftedStore.dispatch(liftAction(action));
|
||||||
|
return action;
|
||||||
|
},
|
||||||
|
getState() {
|
||||||
|
return unliftState(liftedStore.getState());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action creators to change the DevTools state.
|
||||||
|
*/
|
||||||
|
export const ActionCreators = {
|
||||||
|
reset() {
|
||||||
|
return { type: ActionTypes.RESET };
|
||||||
|
},
|
||||||
|
rollback() {
|
||||||
|
return { type: ActionTypes.ROLLBACK };
|
||||||
|
},
|
||||||
|
commit() {
|
||||||
|
return { type: ActionTypes.COMMIT };
|
||||||
|
},
|
||||||
|
sweep() {
|
||||||
|
return { type: ActionTypes.SWEEP };
|
||||||
|
},
|
||||||
|
toggleAction(index) {
|
||||||
|
return { type: ActionTypes.TOGGLE_ACTION, index };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redux DevTools middleware.
|
||||||
|
*/
|
||||||
|
export default function devTools() {
|
||||||
|
return next => (reducer, initialState) => {
|
||||||
|
const liftedReducer = liftReducer(reducer, initialState);
|
||||||
|
const liftedStore = next(liftedReducer);
|
||||||
|
const store = unliftStore(liftedStore);
|
||||||
|
return store;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as devTools } from './devTools';
|
||||||
|
export { default as persistState } from './persistState';
|
39
src/persistState.js
Normal file
39
src/persistState.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
export default function persistState(sessionId) {
|
||||||
|
if (!sessionId) {
|
||||||
|
return next => (...args) => next(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next => (reducer, initialState) => {
|
||||||
|
const key = `redux-dev-session-${sessionId}`;
|
||||||
|
|
||||||
|
let finalInitialState;
|
||||||
|
try {
|
||||||
|
finalInitialState = JSON.parse(localStorage.getItem(key)) || initialState;
|
||||||
|
next(reducer, initialState);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not read debug session from localStorage:', e);
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
} finally {
|
||||||
|
finalInitialState = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = next(reducer, finalInitialState);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...store,
|
||||||
|
dispatch(action) {
|
||||||
|
store.dispatch(action);
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem(key, JSON.stringify(store.getState()));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not write debug session from localStorage:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
51
src/react/DebugPanel.js
Normal file
51
src/react/DebugPanel.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
|
export function getDefaultStyle(props) {
|
||||||
|
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: 999,
|
||||||
|
fontSize: 17,
|
||||||
|
overflow: 'scroll',
|
||||||
|
opacity: 0.92,
|
||||||
|
background: 'black',
|
||||||
|
color: 'white',
|
||||||
|
padding: '1em',
|
||||||
|
left: left ? 0 : undefined,
|
||||||
|
right: right ? 0 : undefined,
|
||||||
|
top: top ? 0 : undefined,
|
||||||
|
bottom: bottom ? 0 : undefined,
|
||||||
|
maxHeight: (bottom && top) ? '100%' : '20%',
|
||||||
|
maxWidth: (left && right) ? '100%' : '20%',
|
||||||
|
wordWrap: 'break-word'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DebugPanel {
|
||||||
|
static propTypes = {
|
||||||
|
left: PropTypes.bool,
|
||||||
|
right: PropTypes.bool,
|
||||||
|
bottom: PropTypes.bool,
|
||||||
|
top: PropTypes.bool,
|
||||||
|
getStyle: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
getStyle: getDefaultStyle
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={this.props.getStyle(this.props)}>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
130
src/react/LogMonitor.js
Normal file
130
src/react/LogMonitor.js
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import React, { PropTypes, findDOMNode } from 'react';
|
||||||
|
import LogMonitorEntry from './LogMonitorEntry';
|
||||||
|
|
||||||
|
export default class LogMonitor {
|
||||||
|
static propTypes = {
|
||||||
|
computedStates: PropTypes.array.isRequired,
|
||||||
|
stagedActions: PropTypes.array.isRequired,
|
||||||
|
skippedActions: PropTypes.object.isRequired,
|
||||||
|
reset: PropTypes.func.isRequired,
|
||||||
|
commit: PropTypes.func.isRequired,
|
||||||
|
rollback: PropTypes.func.isRequired,
|
||||||
|
sweep: PropTypes.func.isRequired,
|
||||||
|
toggleAction: PropTypes.func.isRequired,
|
||||||
|
select: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
select: (state) => state
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.stagedActions.length < nextProps.stagedActions.length) {
|
||||||
|
const scrollableNode = findDOMNode(this).parentElement;
|
||||||
|
const { scrollTop, offsetHeight, scrollHeight } = scrollableNode;
|
||||||
|
|
||||||
|
this.scrollDown = Math.abs(
|
||||||
|
scrollHeight - (scrollTop + offsetHeight)
|
||||||
|
) < 20;
|
||||||
|
} else {
|
||||||
|
this.scrollDown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (
|
||||||
|
prevProps.stagedActions.length < this.props.stagedActions.length &&
|
||||||
|
this.scrollDown
|
||||||
|
) {
|
||||||
|
const scrollableNode = findDOMNode(this).parentElement;
|
||||||
|
const { offsetHeight, scrollHeight } = scrollableNode;
|
||||||
|
|
||||||
|
scrollableNode.scrollTop = scrollHeight - offsetHeight;
|
||||||
|
this.scrollDown = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRollback() {
|
||||||
|
this.props.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSweep() {
|
||||||
|
this.props.sweep();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCommit() {
|
||||||
|
this.props.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToggleAction(index) {
|
||||||
|
this.props.toggleAction(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReset() {
|
||||||
|
this.props.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const elements = [];
|
||||||
|
const { skippedActions, stagedActions, computedStates, select } = this.props;
|
||||||
|
|
||||||
|
for (let i = 0; i < stagedActions.length; i++) {
|
||||||
|
const action = stagedActions[i];
|
||||||
|
const { state, error } = computedStates[i];
|
||||||
|
|
||||||
|
elements.push(
|
||||||
|
<LogMonitorEntry key={i}
|
||||||
|
index={i}
|
||||||
|
select={select}
|
||||||
|
action={action}
|
||||||
|
state={state}
|
||||||
|
collapsed={skippedActions[i]}
|
||||||
|
error={error}
|
||||||
|
onActionClick={::this.handleToggleAction} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
position: 'relative'
|
||||||
|
}}>
|
||||||
|
<div>
|
||||||
|
<a onClick={::this.handleReset}
|
||||||
|
style={{ textDecoration: 'underline', cursor: 'hand' }}>
|
||||||
|
Reset
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{elements}
|
||||||
|
<div>
|
||||||
|
{computedStates.length > 1 &&
|
||||||
|
<a onClick={::this.handleRollback}
|
||||||
|
style={{ textDecoration: 'underline', cursor: 'hand' }}>
|
||||||
|
Rollback
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
{Object.keys(skippedActions).some(key => skippedActions[key]) &&
|
||||||
|
<span>
|
||||||
|
{' • '}
|
||||||
|
<a onClick={::this.handleSweep}
|
||||||
|
style={{ textDecoration: 'underline', cursor: 'hand' }}>
|
||||||
|
Sweep
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{computedStates.length > 1 &&
|
||||||
|
<span>
|
||||||
|
<span>
|
||||||
|
{' • '}
|
||||||
|
</span>
|
||||||
|
<a onClick={::this.handleCommit}
|
||||||
|
style={{ textDecoration: 'underline', cursor: 'hand' }}>
|
||||||
|
Commit
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
121
src/react/LogMonitorEntry.js
Normal file
121
src/react/LogMonitorEntry.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class LogMonitorEntry {
|
||||||
|
static propTypes = {
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
state: PropTypes.object.isRequired,
|
||||||
|
action: PropTypes.object.isRequired,
|
||||||
|
select: PropTypes.func.isRequired,
|
||||||
|
error: PropTypes.string,
|
||||||
|
onActionClick: PropTypes.func.isRequired,
|
||||||
|
collapsed: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
printState(state, error) {
|
||||||
|
let errorText = error;
|
||||||
|
if (!errorText) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(this.props.select(state));
|
||||||
|
} catch (err) {
|
||||||
|
errorText = 'Error selecting state.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span style={{
|
||||||
|
fontStyle: 'italic'
|
||||||
|
}}>
|
||||||
|
({errorText})
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActionClick() {
|
||||||
|
const { index, onActionClick } = this.props;
|
||||||
|
if (index > 0) {
|
||||||
|
onActionClick(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { index, error, action, state, collapsed } = this.props;
|
||||||
|
const { r, g, b } = colorFromString(action.type);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
textDecoration: collapsed ? 'line-through' : 'none'
|
||||||
|
}}>
|
||||||
|
<a onClick={::this.handleActionClick}
|
||||||
|
style={{
|
||||||
|
opacity: collapsed ? 0.5 : 1,
|
||||||
|
marginTop: '1em',
|
||||||
|
display: 'block',
|
||||||
|
paddingBottom: '1em',
|
||||||
|
paddingTop: '1em',
|
||||||
|
color: `rgb(${r}, ${g}, ${b})`,
|
||||||
|
cursor: (index > 0) ? 'hand' : 'default',
|
||||||
|
WebkitUserSelect: 'none'
|
||||||
|
}}>
|
||||||
|
{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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
6
src/react/index.js
Normal file
6
src/react/index.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import React from 'react';
|
||||||
|
import createDevTools from '../createDevTools';
|
||||||
|
|
||||||
|
export const DevTools = createDevTools(React);
|
||||||
|
export { default as LogMonitor } from './LogMonitor';
|
||||||
|
export { default as DebugPanel } from './DebugPanel';
|
1
src/react/native/index.js
Normal file
1
src/react/native/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// TODO
|
Loading…
Reference in New Issue
Block a user