diff --git a/examples/counter/package.json b/examples/counter/package.json index 2e82f7a4..cbc4daa6 100644 --- a/examples/counter/package.json +++ b/examples/counter/package.json @@ -26,7 +26,7 @@ "babel-core": "^5.6.18", "babel-loader": "^5.1.4", "node-libs-browser": "^0.5.2", - "react-dock": "^0.1.0", + "react-dock": "^0.2.1", "react-hot-loader": "^1.3.0", "redux-devtools": "^3.0.0-alpha-8", "redux-devtools-log-monitor": "^1.0.0-alpha-8", diff --git a/examples/counter/src/containers/DevTools.js b/examples/counter/src/containers/DevTools.js index d1b15355..7a574375 100644 --- a/examples/counter/src/containers/DevTools.js +++ b/examples/counter/src/containers/DevTools.js @@ -4,7 +4,7 @@ import LogMonitor from 'redux-devtools-log-monitor'; import DockMonitor from '../dock/DockMonitor'; export default createDevTools( - + ); diff --git a/examples/counter/src/dock/DockMonitor.js b/examples/counter/src/dock/DockMonitor.js index 147be38d..19db0893 100644 --- a/examples/counter/src/dock/DockMonitor.js +++ b/examples/counter/src/dock/DockMonitor.js @@ -1,41 +1,44 @@ -// -// TODO: extract to a separate project. -// - -import React, { cloneElement, Children, Component, PropTypes } from 'react'; +import React, { cloneElement, Component, PropTypes } from 'react'; import Dock from 'react-dock'; -import { combineReducers } from 'redux'; - -const POSITIONS = ['left', 'top', 'right', 'bottom']; +import { POSITIONS } from './constants'; +import { toggleVisibility, changePosition, changeSize } from './actions'; +import reducer from './reducers'; export default class DockMonitor extends Component { + static reducer = reducer; + static propTypes = { defaultPosition: PropTypes.oneOf(POSITIONS).isRequired, defaultIsVisible: PropTypes.bool.isRequired, + defaultSize: PropTypes.number.isRequired, toggleVisibilityShortcut: PropTypes.string.isRequired, changePositionShortcut: PropTypes.string.isRequired, + children: PropTypes.element, + dispatch: PropTypes.func, monitorState: PropTypes.shape({ position: PropTypes.oneOf(POSITIONS).isRequired, + size: PropTypes.number.isRequired, isVisible: PropTypes.bool.isRequired, - child: PropTypes.any - }), - - monitorActions: PropTypes.shape({ - toggleVisibility: PropTypes.func.isRequired, - changePosition: PropTypes.func.isRequired + childMonitorState: PropTypes.any }) }; static defaultProps = { defaultIsVisible: true, defaultPosition: 'right', + defaultSize: 0.3, toggleVisibilityShortcut: 'H', changePositionShortcut: 'Q' }; - componentDidMount() { + constructor(props) { + super(props); this.handleKeyDown = this.handleKeyDown.bind(this); + this.handleSizeChange = this.handleSizeChange.bind(this); + } + + componentDidMount() { window.addEventListener('keydown', this.handleKeyDown); } @@ -53,81 +56,36 @@ export default class DockMonitor extends Component { const char = String.fromCharCode(key); switch (char.toUpperCase()) { case this.props.toggleVisibilityShortcut.toUpperCase(): - this.props.monitorActions.toggleVisibility(); + this.props.dispatch(toggleVisibility()); break; case this.props.changePositionShortcut.toUpperCase(): - this.props.monitorActions.changePosition(); + this.props.dispatch(changePosition()); break; default: break; } } - render() { - const { - monitorState, - monitorActions, - historyState, - historyActions, - children - } = this.props; + handleSizeChange(requestedSize) { + this.props.dispatch(changeSize(requestedSize)); + } - const { - position, - isVisible - } = monitorState; + render() { + const { monitorState, children, ...rest } = this.props; + const { position, isVisible, size } = monitorState; + const childProps = { + ...rest, + monitorState: monitorState.childMonitorState + }; return ( - {cloneElement(Children.only(children), { - monitorState: monitorState.child, - monitorActions: monitorActions.child, - historyState, - historyActions - })} + {cloneElement(children, childProps)} ); } } - -const TOGGLE_VISIBILITY = '@@redux-devtools/dock/TOGGLE_VISIBILITY'; -function toggleVisibility() { - return { type: TOGGLE_VISIBILITY }; -} - -const CHANGE_POSITION = '@@redux-devtools/dock/CHANGE_POSITION'; -function changePosition() { - return { type: CHANGE_POSITION }; -} - -DockMonitor.setup = function setup(props) { - function position(state = props.defaultPosition, action) { - return (action.type === CHANGE_POSITION) ? - POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : - state; - } - - function isVisible(state = props.defaultIsVisible, action) { - return (action.type === TOGGLE_VISIBILITY) ? - !state : - state; - } - - const child = Children.only(props.children); - const childSetupResult = child.type.setup(child.props); - - return { - reducer: combineReducers({ - position, - isVisible, - child: childSetupResult.reducer - }), - actionCreators: { - toggleVisibility, - changePosition, - child: childSetupResult.actionCreators - } - }; -} diff --git a/examples/counter/src/dock/actions.js b/examples/counter/src/dock/actions.js new file mode 100644 index 00000000..4a743666 --- /dev/null +++ b/examples/counter/src/dock/actions.js @@ -0,0 +1,14 @@ +export const TOGGLE_VISIBILITY = '@@redux-devtools/dock/TOGGLE_VISIBILITY'; +export function toggleVisibility() { + return { type: TOGGLE_VISIBILITY }; +} + +export const CHANGE_POSITION = '@@redux-devtools/dock/CHANGE_POSITION'; +export function changePosition() { + return { type: CHANGE_POSITION }; +} + +export const CHANGE_SIZE = '@@redux-devtools/dock/CHANGE_SIZE'; +export function changeSize(size) { + return { type: CHANGE_SIZE, size: size }; +} diff --git a/examples/counter/src/dock/constants.js b/examples/counter/src/dock/constants.js new file mode 100644 index 00000000..e935427f --- /dev/null +++ b/examples/counter/src/dock/constants.js @@ -0,0 +1 @@ +export const POSITIONS = ['left', 'top', 'right', 'bottom']; diff --git a/examples/counter/src/dock/reducers.js b/examples/counter/src/dock/reducers.js new file mode 100644 index 00000000..42b7726d --- /dev/null +++ b/examples/counter/src/dock/reducers.js @@ -0,0 +1,34 @@ +import { CHANGE_POSITION, CHANGE_SIZE, TOGGLE_VISIBILITY } from './actions'; +import { POSITIONS } from './constants'; + +function position(state = props.defaultPosition, action, props) { + return (action.type === CHANGE_POSITION) ? + POSITIONS[(POSITIONS.indexOf(state) + 1) % POSITIONS.length] : + state; +} + +function size(state = props.defaultSize, action, props) { + return (action.type === CHANGE_SIZE) ? + action.size : + state; +} + +function isVisible(state = props.defaultIsVisible, action, props) { + return (action.type === TOGGLE_VISIBILITY) ? + !state : + state; +} + +function childMonitorState(state, action, props) { + const child = props.children; + return child.type.reducer(state, action, child.props); +} + +export default function reducer(state = {}, action, props) { + return { + position: position(state.position, action, props), + isVisible: isVisible(state.isVisible, action, props), + size: size(state.size, action, props), + childMonitorState: childMonitorState(state.childMonitorState, action, props) + }; +} diff --git a/src/bindActionCreatorsDeep.js b/src/bindActionCreatorsDeep.js deleted file mode 100644 index 8c8626a6..00000000 --- a/src/bindActionCreatorsDeep.js +++ /dev/null @@ -1,20 +0,0 @@ -import { bindActionCreators } from 'redux'; - -export default function bindActionCreatorsDeep(actionCreators, dispatch) { - return Object.keys(actionCreators).reduce((result, key) => { - if (!actionCreators[key]) { - return result; - } - switch (typeof actionCreators[key]) { - case 'object': - result[key] = bindActionCreatorsDeep(actionCreators[key], dispatch); - break; - case 'function': - result[key] = bindActionCreators(actionCreators[key], dispatch); - break; - default: - break; - } - return result; - }, {}); -} diff --git a/src/createDevTools.js b/src/createDevTools.js index fb1245b5..7935ec52 100644 --- a/src/createDevTools.js +++ b/src/createDevTools.js @@ -1,39 +1,30 @@ import React, { Children, Component, PropTypes } from 'react'; import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import bindActionCreatorsDeep from './bindActionCreatorsDeep'; -import instrument, { ActionCreators as historyActionCreators } from './instrument'; +import instrument from './instrument'; export default function createDevTools(children) { - const child = Children.only(children); - const { type: Monitor } = child; - const { reducer, actionCreators } = Monitor.setup(child.props); + const monitorElement = Children.only(children); + const monitorProps = monitorElement.props; + const Monitor = monitorElement.type; + + const enhancer = instrument((state, action) => + Monitor.reducer(state, action, monitorProps) + ); function mapStateToProps(state) { return { - historyState: state.historyState, + ...state.historyState, monitorState: state.monitorState }; } - - function mapDispatchToProps(dispatch) { - return { - historyActions: bindActionCreators(historyActionCreators, dispatch), - monitorActions: bindActionCreatorsDeep(actionCreators, dispatch) - }; - } - - const ConnectedMonitor = connect( - mapStateToProps, - mapDispatchToProps - )(Monitor); + const ConnectedMonitor = connect(mapStateToProps)(Monitor); return class DevTools extends Component { static contextTypes = { store: PropTypes.object.isRequired }; - static instrument = () => instrument(reducer); + static instrument = () => enhancer; constructor(props, context) { super(props, context); @@ -42,7 +33,7 @@ export default function createDevTools(children) { render() { return ( - ); } diff --git a/src/index.js b/src/index.js index 5b3c3a35..f302dc55 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ -export { default as instrument, ActionTypes } from './instrument'; +export { default as instrument, ActionCreators, ActionTypes } from './instrument'; export { default as persistState } from './persistState'; export { default as createDevTools } from './createDevTools';