├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── examples ├── todomvc-livewire │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── actions │ │ └── TodoActions.js │ ├── components │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── MainSection.js │ │ ├── TodoItem.js │ │ └── TodoTextInput.js │ ├── constants │ │ ├── ActionTypes.js │ │ └── TodoFilters.js │ ├── containers │ │ ├── App.js │ │ ├── DevTools.js │ │ ├── Root.dev.js │ │ ├── Root.js │ │ ├── Root.prod.js │ │ └── TodoApp.js │ ├── index.html │ ├── index.js │ ├── index.template.ejs │ ├── package.json │ ├── reducers │ │ ├── index.js │ │ └── todos.js │ ├── server.js │ ├── store │ │ ├── configureStore.dev.js │ │ ├── configureStore.js │ │ └── configureStore.prod.js │ ├── webpack.config.dist.js │ └── webpack.config.js └── todomvc │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── actions │ └── TodoActions.js │ ├── components │ ├── Footer.js │ ├── Header.js │ ├── MainSection.js │ ├── TodoItem.js │ └── TodoTextInput.js │ ├── constants │ ├── ActionTypes.js │ └── TodoFilters.js │ ├── containers │ ├── App.js │ ├── DevTools.js │ ├── Root.dev.js │ ├── Root.js │ ├── Root.prod.js │ └── TodoApp.js │ ├── index.html │ ├── index.js │ ├── index.template.ejs │ ├── package.json │ ├── reducers │ ├── index.js │ └── todos.js │ ├── server.js │ ├── store │ ├── configureStore.dev.js │ ├── configureStore.js │ └── configureStore.prod.js │ ├── webpack.config.dist.js │ └── webpack.config.js ├── lib ├── action │ └── index.js ├── button │ └── index.js ├── index.js └── utils │ └── diff-state.js ├── package.json ├── src ├── action │ └── index.js ├── button │ └── index.js ├── index.js └── utils │ └── diff-state.js └── test ├── action └── index.spec.js ├── index.spec.js ├── mocha.opts ├── test-utils └── dom.js └── utils └── diff-state.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-1", 5 | "react" 6 | ] 7 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "rules": { 5 | "no-unused-vars": 1, 6 | "no-use-before-define": 0, 7 | "key-spacing": 0, 8 | "no-multi-spaces": 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: node_js 3 | env: 4 | - CXX=g++-4.8 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-4.8 11 | node_js: 12 | - "4" 13 | - "5" 14 | script: 15 | - npm run lint 16 | - npm test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redux DevTools – Diff Monitor 2 | 3 | [![build status](https://img.shields.io/travis/whetstone/redux-devtools-diff-monitor.svg?style=flat-square)](http://travis-ci.org/whetstone/redux-devtools-diff-monitor) 4 | [![npm version](https://img.shields.io/npm/v/redux-devtools-diff-monitor.svg?style=flat-square)](https://www.npmjs.com/package/redux-devtools-diff-monitor) 5 | [![npm downloads](https://img.shields.io/npm/dm/redux-devtools-diff-monitor.svg?style=flat-square)](https://www.npmjs.com/package/redux-devtools-diff-monitor) 6 | 7 | 5.0 has been released with support for React 15. It contains other improvements to performance (such as calculating diff only when an action is expanded) and some cosmetic changes. 8 | 9 | This project provides an alternate monitor for Redux DevTools. The primary goal of this monitor is to highlight the 10 | changes to an application's state from action to action. This tool includes the main features from the default DevTools 11 | monitor (rollback, commit, reset and individual action toggles). 12 | 13 | ![Imgur](http://i.imgur.com/rvCR9OQ.png) 14 | 15 | ### Installation Examples 16 | 17 | See the Redux Devtools [documentation](https://github.com/gaearon/redux-devtools#create-a-devtools-component) 18 | for full details about how to use monitors. 19 | 20 | #### Standalone Monitor 21 | 22 | To use Diff Monitor by itself along with Redux Devtools, simply pass it to the `createDevTools` function directly. 23 | 24 | Install from npm: `npm install --save-dev redux-devtools redux-devtools-diff-monitor` 25 | 26 | ```javascript 27 | import React from 'react'; 28 | import { createDevTools } from 'redux-devtools'; 29 | import DiffMonitor from 'redux-devtools-diff-monitor'; 30 | 31 | export default createDevTools( 32 | 33 | ); 34 | ``` 35 | 36 | #### Using DockMonitor 37 | 38 | The [DockMonitor](https://github.com/gaearon/redux-devtools-dock-monitor) component provides common docking 39 | functionality that makes monitors easier to work with. See the 40 | [documentation](https://github.com/gaearon/redux-devtools-dock-monitor#readme) for additional details. 41 | 42 | Install from npm: `npm install --save-dev redux-devtools-dock-monitor redux-devtools-diff-monitor` 43 | 44 | ```javascript 45 | import React from 'react'; 46 | import { createDevTools } from 'redux-devtools'; 47 | import DiffMonitor from 'redux-devtools-diff-monitor'; 48 | import DockMonitor from 'redux-devtools-dock-monitor'; 49 | 50 | export default const DevTools = createDevTools( 51 | 55 | 56 | 57 | ); 58 | ``` 59 | 60 | ### Usage 61 | 62 | - New actions appear at the top of the monitor as they occur. 63 | - Actions will be minimized by default; actions shown in green are causing a state mutation. 64 | - Click an action name to expand its pane to view the state mutations the action caused. 65 | - Click "disable" next to any action name to ignore that action and roll back the state mutations that action caused. 66 | - As in the default Redux DevTools, click 'Commit' to reset the monitor and set the current app state as the rollback 67 | point. If you click rollback after clicking commit, actions will be replayed through the commit point. 68 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015-loose", "stage-0", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .divshot-cache 3 | divshot.json 4 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/README.md: -------------------------------------------------------------------------------- 1 | # Redux DevTools Counter example 2 | 3 | This example is adapted from `https://github.com/gaearon/redux-devtools/tree/master/examples/todomvc` to demonstrate 4 | the use of the Redux Devtools Diff Monitor. 5 | 6 | ## Running Example 7 | 8 | First, clone the project: 9 | 10 | ``` 11 | git clone https://github.com/whetstone/redux-devtools-diff-monitor.git 12 | ``` 13 | 14 | Then install the dependencies in the root folder: 15 | 16 | ``` 17 | cd redux-devtools-diff-monitor 18 | npm install 19 | ``` 20 | 21 | Install the dependencies in the example folder: 22 | 23 | ``` 24 | cd examples/todomvc 25 | npm install 26 | ``` 27 | 28 | Finally, run the project: 29 | 30 | ``` 31 | npm start 32 | open http://localhost:3000 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | export function addTodo(text) { 4 | return { 5 | type: types.ADD_TODO, 6 | text 7 | }; 8 | } 9 | 10 | export function deleteTodo(id) { 11 | return { 12 | type: types.DELETE_TODO, 13 | id 14 | }; 15 | } 16 | 17 | export function editTodo(id, text) { 18 | return { 19 | type: types.EDIT_TODO, 20 | id, 21 | text 22 | }; 23 | } 24 | 25 | export function markTodo(id) { 26 | return { 27 | type: types.MARK_TODO, 28 | id 29 | }; 30 | } 31 | 32 | export function markAll() { 33 | return { 34 | type: types.MARK_ALL 35 | }; 36 | } 37 | 38 | export function clearMarked() { 39 | return { 40 | type: types.CLEAR_MARKED 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import classnames from 'classnames'; 3 | import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters'; 4 | 5 | const FILTER_TITLES = { 6 | [SHOW_ALL]: 'All', 7 | [SHOW_UNMARKED]: 'Active', 8 | [SHOW_MARKED]: 'Completed' 9 | }; 10 | 11 | export default class Footer extends Component { 12 | static propTypes = { 13 | markedCount: PropTypes.number.isRequired, 14 | unmarkedCount: PropTypes.number.isRequired, 15 | filter: PropTypes.string.isRequired, 16 | onClearMarked: PropTypes.func.isRequired, 17 | onShow: PropTypes.func.isRequired 18 | } 19 | 20 | render() { 21 | return ( 22 | 33 | ); 34 | } 35 | 36 | renderTodoCount() { 37 | const { unmarkedCount } = this.props; 38 | const itemWord = unmarkedCount === 1 ? 'item' : 'items'; 39 | 40 | return ( 41 | 42 | {unmarkedCount || 'No'} {itemWord} left 43 | 44 | ); 45 | } 46 | 47 | renderFilterLink(filter) { 48 | const title = FILTER_TITLES[filter]; 49 | const { filter: selectedFilter, onShow } = this.props; 50 | 51 | return ( 52 | onShow(filter)}> 55 | {title} 56 | 57 | ); 58 | } 59 | 60 | renderClearButton() { 61 | const { markedCount, onClearMarked } = this.props; 62 | if (markedCount > 0) { 63 | return ( 64 | 68 | ); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import TodoTextInput from './TodoTextInput'; 3 | 4 | export default class Header extends Component { 5 | static propTypes = { 6 | addTodo: PropTypes.func.isRequired 7 | }; 8 | 9 | handleSave(text) { 10 | if (text.length !== 0) { 11 | this.props.addTodo(text); 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |

todos

19 | 22 |
23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/todomvc-livewire/components/MainSection.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import TodoItem from './TodoItem'; 3 | import Footer from './Footer'; 4 | import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters'; 5 | 6 | const TODO_FILTERS = { 7 | [SHOW_ALL]: () => true, 8 | [SHOW_UNMARKED]: todo => !todo.marked, 9 | [SHOW_MARKED]: todo => todo.marked 10 | }; 11 | 12 | export default class MainSection extends Component { 13 | static propTypes = { 14 | todos: PropTypes.array.isRequired, 15 | actions: PropTypes.object.isRequired 16 | }; 17 | 18 | constructor(props, context) { 19 | super(props, context); 20 | this.state = { filter: SHOW_ALL }; 21 | } 22 | 23 | handleClearMarked() { 24 | const atLeastOneMarked = this.props.todos.some(todo => todo.marked); 25 | if (atLeastOneMarked) { 26 | this.props.actions.clearMarked(); 27 | } 28 | } 29 | 30 | handleShow(filter) { 31 | this.setState({ filter }); 32 | } 33 | 34 | render() { 35 | const { todos, actions } = this.props; 36 | const { filter } = this.state; 37 | 38 | const filteredTodos = todos.filter(TODO_FILTERS[filter]); 39 | const markedCount = todos.reduce((count, todo) => 40 | todo.marked ? count + 1 : count, 41 | 0 42 | ); 43 | 44 | return ( 45 |
46 | {this.renderToggleAll(markedCount)} 47 | 52 | {this.renderFooter(markedCount)} 53 |
54 | ); 55 | } 56 | 57 | renderToggleAll(markedCount) { 58 | const { todos, actions } = this.props; 59 | if (todos.length > 0) { 60 | return ( 61 | 65 | ); 66 | } 67 | } 68 | 69 | renderFooter(markedCount) { 70 | const { todos } = this.props; 71 | const { filter } = this.state; 72 | const unmarkedCount = todos.length - markedCount; 73 | 74 | if (todos.length) { 75 | return ( 76 |