mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-11-04 01:47:25 +03:00 
			
		
		
		
	Add redux-slider-monitor (#434)
* Add redux-slider-monitor * Fix example configuration of redux-slider-monitor * Fix lint errors * CI: Run build:all before lint
This commit is contained in:
		
							parent
							
								
									e6fdfb9c9e
								
							
						
					
					
						commit
						476e37a875
					
				| 
						 | 
				
			
			@ -8,6 +8,6 @@ cache:
 | 
			
		|||
  directories:
 | 
			
		||||
    - "node_modules"
 | 
			
		||||
script:
 | 
			
		||||
  - npm run lint
 | 
			
		||||
  - npm run build:all
 | 
			
		||||
  - npm run lint
 | 
			
		||||
  - npm test
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								packages/redux-slider-monitor/.babelrc
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								packages/redux-slider-monitor/.babelrc
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
{
 | 
			
		||||
  "presets": ["es2015", "stage-0", "react"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/redux-slider-monitor/.eslintignore
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								packages/redux-slider-monitor/.eslintignore
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
lib
 | 
			
		||||
**/node_modules
 | 
			
		||||
examples/**/dist
 | 
			
		||||
							
								
								
									
										22
									
								
								packages/redux-slider-monitor/.eslintrc
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								packages/redux-slider-monitor/.eslintrc
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
{
 | 
			
		||||
  "extends": "eslint-config-airbnb",
 | 
			
		||||
  "env": {
 | 
			
		||||
    "browser": true,
 | 
			
		||||
    "mocha": true,
 | 
			
		||||
    "node": true
 | 
			
		||||
  },
 | 
			
		||||
  "parser": "babel-eslint",
 | 
			
		||||
  "rules": {
 | 
			
		||||
    "comma-dangle": [2, "never"],
 | 
			
		||||
    "jsx-quotes": [2, "prefer-single"],
 | 
			
		||||
    "react/jsx-uses-react": 2,
 | 
			
		||||
    "react/jsx-uses-vars": 2,
 | 
			
		||||
    "react/react-in-jsx-scope": 2,
 | 
			
		||||
    "react/sort-comp": 0,
 | 
			
		||||
    "react/forbid-prop-types": 0,
 | 
			
		||||
    "import/no-extraneous-dependencies": 0,
 | 
			
		||||
    "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
 | 
			
		||||
    "jsx-a11y/no-static-element-interactions": 0
 | 
			
		||||
  },
 | 
			
		||||
  "plugins": ["react"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								packages/redux-slider-monitor/LICENSE.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										21
									
								
								packages/redux-slider-monitor/LICENSE.md
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2016 Cale Newman
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										61
									
								
								packages/redux-slider-monitor/README.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										61
									
								
								packages/redux-slider-monitor/README.md
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
## Redux Slider Monitor
 | 
			
		||||
 | 
			
		||||
[](https://www.npmjs.com/package/redux-slider-monitor)
 | 
			
		||||
 | 
			
		||||
A custom monitor for use with [Redux DevTools](https://github.com/gaearon/redux-devtools).
 | 
			
		||||
 | 
			
		||||
It uses a slider based on [react-slider](https://github.com/mpowaga/react-slider) to slide between different recorded actions. It also features play/pause/step-through, which is inspired by some very cool [Elm](http://elm-lang.org/) [examples](http://elm-lang.org/blog/time-travel-made-easy).
 | 
			
		||||
 | 
			
		||||
[Try out the demo!](https://calesce.github.io/redux-slider-monitor/?debug_session=123)
 | 
			
		||||
 | 
			
		||||
<image src="https://s3.amazonaws.com/f.cl.ly/items/1I3P222C3N2R1M2y1K3b/Screen%20Recording%202015-12-22%20at%2007.20%20PM.gif?v=1b6267e7" width='800'>
 | 
			
		||||
 | 
			
		||||
### Installation
 | 
			
		||||
 | 
			
		||||
```npm install redux-slider-monitor```
 | 
			
		||||
 | 
			
		||||
### Recommended Usage
 | 
			
		||||
 | 
			
		||||
Use with [`DockMonitor`](https://github.com/gaearon/redux-devtools-dock-monitor)
 | 
			
		||||
```javascript
 | 
			
		||||
<DockMonitor toggleVisibilityKey='ctrl-h'
 | 
			
		||||
             changePositionKey='ctrl-q'
 | 
			
		||||
             defaultPosition='bottom'
 | 
			
		||||
             defaultSize={0.15}>
 | 
			
		||||
  <SliderMonitor keyboardEnabled />
 | 
			
		||||
</DockMonitor>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Dispatch some Redux actions. Use the slider to navigate between the state changes.
 | 
			
		||||
 | 
			
		||||
Click the play/pause buttons to watch the state changes over time, or step backward or forward in state time with the left/right arrow buttons. Change replay speeds with the ```1x``` button, and "Live" will replay actions with the same time intervals in which they originally were dispatched.
 | 
			
		||||
 | 
			
		||||
## Keyboard shortcuts
 | 
			
		||||
 | 
			
		||||
Pass the ```keyboardEnabled``` prop to use these shortcuts
 | 
			
		||||
 | 
			
		||||
```ctrl+j```: play/pause
 | 
			
		||||
 | 
			
		||||
```ctrl+[```: step backward
 | 
			
		||||
 | 
			
		||||
```ctrl+]```: step forward
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Running Examples
 | 
			
		||||
 | 
			
		||||
You can do this:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
git clone https://github.com/calesce/redux-slider-monitor.git
 | 
			
		||||
cd redux-slider-monitor
 | 
			
		||||
npm install
 | 
			
		||||
 | 
			
		||||
cd examples/todomvc
 | 
			
		||||
npm install
 | 
			
		||||
npm start
 | 
			
		||||
open http://localhost:3000
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### License
 | 
			
		||||
 | 
			
		||||
MIT
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/redux-slider-monitor/examples/todomvc/.babelrc
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										10
									
								
								packages/redux-slider-monitor/examples/todomvc/.babelrc
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
{
 | 
			
		||||
  "presets": [
 | 
			
		||||
    ["es2015", { "modules": false }],
 | 
			
		||||
    "stage-0",
 | 
			
		||||
    "react"
 | 
			
		||||
  ],
 | 
			
		||||
  "plugins": [
 | 
			
		||||
    "react-hot-loader/babel"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/redux-slider-monitor/examples/todomvc/README.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								packages/redux-slider-monitor/examples/todomvc/README.md
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
# Redux DevTools TodoMVC example
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
1. Install dependencies: `npm i`
 | 
			
		||||
2. Start the development server: `npm start`
 | 
			
		||||
							
								
								
									
										42
									
								
								packages/redux-slider-monitor/examples/todomvc/actions/TodoActions.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										42
									
								
								packages/redux-slider-monitor/examples/todomvc/actions/TodoActions.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
import * as types from '../constants/ActionTypes';
 | 
			
		||||
 | 
			
		||||
export function addTodo(text) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.ADD_TODO,
 | 
			
		||||
    text
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function deleteTodo(id) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.DELETE_TODO,
 | 
			
		||||
    id
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function editTodo(id, text) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.EDIT_TODO,
 | 
			
		||||
    id,
 | 
			
		||||
    text
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function markTodo(id) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.MARK_TODO,
 | 
			
		||||
    id
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function markAll() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.MARK_ALL
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function clearMarked() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: types.CLEAR_MARKED
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								packages/redux-slider-monitor/examples/todomvc/components/Footer.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										72
									
								
								packages/redux-slider-monitor/examples/todomvc/components/Footer.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
import React, { Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
 | 
			
		||||
 | 
			
		||||
const FILTER_TITLES = {
 | 
			
		||||
  [SHOW_ALL]: 'All',
 | 
			
		||||
  [SHOW_UNMARKED]: 'Active',
 | 
			
		||||
  [SHOW_MARKED]: 'Completed'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default class Footer extends Component {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    markedCount: PropTypes.number.isRequired,
 | 
			
		||||
    unmarkedCount: PropTypes.number.isRequired,
 | 
			
		||||
    filter: PropTypes.string.isRequired,
 | 
			
		||||
    onClearMarked: PropTypes.func.isRequired,
 | 
			
		||||
    onShow: PropTypes.func.isRequired
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
      <footer className='footer'>
 | 
			
		||||
        {this.renderTodoCount()}
 | 
			
		||||
        <ul className='filters'>
 | 
			
		||||
          {[SHOW_ALL, SHOW_UNMARKED, SHOW_MARKED].map(filter => (
 | 
			
		||||
            <li key={filter}>{this.renderFilterLink(filter)}</li>
 | 
			
		||||
          ))}
 | 
			
		||||
        </ul>
 | 
			
		||||
        {this.renderClearButton()}
 | 
			
		||||
      </footer>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderTodoCount() {
 | 
			
		||||
    const { unmarkedCount } = this.props;
 | 
			
		||||
    const itemWord = unmarkedCount === 1 ? 'item' : 'items';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <span className='todo-count'>
 | 
			
		||||
        <strong>{unmarkedCount || 'No'}</strong> {itemWord} left
 | 
			
		||||
      </span>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderFilterLink(filter) {
 | 
			
		||||
    const title = FILTER_TITLES[filter];
 | 
			
		||||
    const { filter: selectedFilter, onShow } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <a
 | 
			
		||||
        className={classnames({ selected: filter === selectedFilter })}
 | 
			
		||||
        style={{ cursor: 'hand' }}
 | 
			
		||||
        onClick={() => onShow(filter)}
 | 
			
		||||
      >
 | 
			
		||||
        {title}
 | 
			
		||||
      </a>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderClearButton() {
 | 
			
		||||
    const { markedCount, onClearMarked } = this.props;
 | 
			
		||||
    if (markedCount > 0) {
 | 
			
		||||
      return (
 | 
			
		||||
        <button className='clear-completed' onClick={onClearMarked}>
 | 
			
		||||
          Clear completed
 | 
			
		||||
        </button>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								packages/redux-slider-monitor/examples/todomvc/components/Header.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										28
									
								
								packages/redux-slider-monitor/examples/todomvc/components/Header.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import React, { Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import TodoTextInput from './TodoTextInput';
 | 
			
		||||
 | 
			
		||||
export default class Header extends Component {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    addTodo: PropTypes.func.isRequired
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleSave = (text) => {
 | 
			
		||||
    if (text.length !== 0) {
 | 
			
		||||
      this.props.addTodo(text);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
      <header className='header'>
 | 
			
		||||
        <h1>todos</h1>
 | 
			
		||||
        <TodoTextInput
 | 
			
		||||
          newTodo
 | 
			
		||||
          onSave={this.handleSave}
 | 
			
		||||
          placeholder='What needs to be done?'
 | 
			
		||||
        />
 | 
			
		||||
      </header>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								packages/redux-slider-monitor/examples/todomvc/components/MainSection.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										90
									
								
								packages/redux-slider-monitor/examples/todomvc/components/MainSection.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
import React, { Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import TodoItem from './TodoItem';
 | 
			
		||||
import Footer from './Footer';
 | 
			
		||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
 | 
			
		||||
 | 
			
		||||
const TODO_FILTERS = {
 | 
			
		||||
  [SHOW_ALL]: () => true,
 | 
			
		||||
  [SHOW_UNMARKED]: todo => !todo.marked,
 | 
			
		||||
  [SHOW_MARKED]: todo => todo.marked
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default class MainSection extends Component {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    todos: PropTypes.array.isRequired,
 | 
			
		||||
    actions: PropTypes.object.isRequired
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context);
 | 
			
		||||
    this.handleClearMarked = this.handleClearMarked.bind(this);
 | 
			
		||||
    this.handleShow = this.handleShow.bind(this);
 | 
			
		||||
    this.state = { filter: SHOW_ALL };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleClearMarked() {
 | 
			
		||||
    const atLeastOneMarked = this.props.todos.some(todo => todo.marked);
 | 
			
		||||
    if (atLeastOneMarked) {
 | 
			
		||||
      this.props.actions.clearMarked();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleShow(filter) {
 | 
			
		||||
    this.setState({ filter });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { todos, actions } = this.props;
 | 
			
		||||
    const { filter } = this.state;
 | 
			
		||||
 | 
			
		||||
    const filteredTodos = todos.filter(TODO_FILTERS[filter]);
 | 
			
		||||
    const markedCount = todos.reduce((count, todo) => (todo.marked ? count + 1 : count), 0);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <section className='main'>
 | 
			
		||||
        {this.renderToggleAll(markedCount)}
 | 
			
		||||
        <ul className='todo-list'>
 | 
			
		||||
          {filteredTodos.map(todo => (
 | 
			
		||||
            <TodoItem key={todo.id} todo={todo} {...actions} />
 | 
			
		||||
          ))}
 | 
			
		||||
        </ul>
 | 
			
		||||
        {this.renderFooter(markedCount)}
 | 
			
		||||
      </section>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderToggleAll(markedCount) {
 | 
			
		||||
    const { todos, actions } = this.props;
 | 
			
		||||
    if (todos.length > 0) {
 | 
			
		||||
      return (
 | 
			
		||||
        <input
 | 
			
		||||
          className='toggle-all'
 | 
			
		||||
          type='checkbox'
 | 
			
		||||
          checked={markedCount === todos.length}
 | 
			
		||||
          onChange={actions.markAll}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderFooter(markedCount) {
 | 
			
		||||
    const { todos } = this.props;
 | 
			
		||||
    const { filter } = this.state;
 | 
			
		||||
    const unmarkedCount = todos.length - markedCount;
 | 
			
		||||
 | 
			
		||||
    if (todos.length) {
 | 
			
		||||
      return (
 | 
			
		||||
        <Footer
 | 
			
		||||
          markedCount={markedCount}
 | 
			
		||||
          unmarkedCount={unmarkedCount}
 | 
			
		||||
          filter={filter}
 | 
			
		||||
          onClearMarked={this.handleClearMarked}
 | 
			
		||||
          onShow={this.handleShow}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								packages/redux-slider-monitor/examples/todomvc/components/TodoItem.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										77
									
								
								packages/redux-slider-monitor/examples/todomvc/components/TodoItem.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
import React, { Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
import TodoTextInput from './TodoTextInput';
 | 
			
		||||
 | 
			
		||||
export default class TodoItem extends Component {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    todo: PropTypes.object.isRequired,
 | 
			
		||||
    editTodo: PropTypes.func.isRequired,
 | 
			
		||||
    deleteTodo: PropTypes.func.isRequired,
 | 
			
		||||
    markTodo: PropTypes.func.isRequired
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context);
 | 
			
		||||
    this.state = {
 | 
			
		||||
      editing: false
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleDoubleClick = () => {
 | 
			
		||||
    this.setState({ editing: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleSave = (id, text) => {
 | 
			
		||||
    if (text.length === 0) {
 | 
			
		||||
      this.props.deleteTodo(id);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.props.editTodo(id, text);
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ editing: false });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { todo, markTodo, deleteTodo } = this.props;
 | 
			
		||||
 | 
			
		||||
    let element;
 | 
			
		||||
    if (this.state.editing) {
 | 
			
		||||
      element = (
 | 
			
		||||
        <TodoTextInput
 | 
			
		||||
          text={todo.text}
 | 
			
		||||
          editing={this.state.editing}
 | 
			
		||||
          onSave={text => this.handleSave(todo.id, text)}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      element = (
 | 
			
		||||
        <div className='view'>
 | 
			
		||||
          <input
 | 
			
		||||
            className='toggle'
 | 
			
		||||
            type='checkbox'
 | 
			
		||||
            checked={todo.marked}
 | 
			
		||||
            onChange={() => markTodo(todo.id)}
 | 
			
		||||
          />
 | 
			
		||||
          <label htmlFor='text' onDoubleClick={this.handleDoubleClick}>
 | 
			
		||||
            {todo.text}
 | 
			
		||||
          </label>
 | 
			
		||||
          <button
 | 
			
		||||
            className='destroy'
 | 
			
		||||
            onClick={() => deleteTodo(todo.id)}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <li
 | 
			
		||||
        className={classnames({
 | 
			
		||||
          completed: todo.marked,
 | 
			
		||||
          editing: this.state.editing
 | 
			
		||||
        })}
 | 
			
		||||
      >
 | 
			
		||||
        {element}
 | 
			
		||||
      </li>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								packages/redux-slider-monitor/examples/todomvc/components/TodoTextInput.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								packages/redux-slider-monitor/examples/todomvc/components/TodoTextInput.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
import React, { Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
 | 
			
		||||
export default class TodoTextInput extends Component {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    onSave: PropTypes.func.isRequired,
 | 
			
		||||
    text: PropTypes.string,
 | 
			
		||||
    placeholder: PropTypes.string,
 | 
			
		||||
    editing: PropTypes.bool,
 | 
			
		||||
    newTodo: PropTypes.bool
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static defaultProps = {
 | 
			
		||||
    text: '',
 | 
			
		||||
    placeholder: '',
 | 
			
		||||
    editing: false,
 | 
			
		||||
    newTodo: false
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context);
 | 
			
		||||
    this.state = {
 | 
			
		||||
      text: this.props.text || ''
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleSubmit = (e) => {
 | 
			
		||||
    const text = e.target.value.trim();
 | 
			
		||||
    if (e.which === 13) {
 | 
			
		||||
      this.props.onSave(text);
 | 
			
		||||
      if (this.props.newTodo) {
 | 
			
		||||
        this.setState({ text: '' });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleChange = (e) => {
 | 
			
		||||
    this.setState({ text: e.target.value });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleBlur = (e) => {
 | 
			
		||||
    if (!this.props.newTodo) {
 | 
			
		||||
      this.props.onSave(e.target.value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
      <input
 | 
			
		||||
        className={classnames({ edit: this.props.editing, 'new-todo': this.props.newTodo })}
 | 
			
		||||
        type='text'
 | 
			
		||||
        placeholder={this.props.placeholder}
 | 
			
		||||
        autoFocus='true'
 | 
			
		||||
        value={this.state.text}
 | 
			
		||||
        onBlur={this.handleBlur}
 | 
			
		||||
        onChange={this.handleChange}
 | 
			
		||||
        onKeyDown={this.handleSubmit}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/redux-slider-monitor/examples/todomvc/constants/ActionTypes.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										6
									
								
								packages/redux-slider-monitor/examples/todomvc/constants/ActionTypes.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
export const ADD_TODO = 'ADD_TODO';
 | 
			
		||||
export const DELETE_TODO = 'DELETE_TODO';
 | 
			
		||||
export const EDIT_TODO = 'EDIT_TODO';
 | 
			
		||||
export const MARK_TODO = 'MARK_TODO';
 | 
			
		||||
export const MARK_ALL = 'MARK_ALL';
 | 
			
		||||
export const CLEAR_MARKED = 'CLEAR_MARKED';
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/redux-slider-monitor/examples/todomvc/constants/TodoFilters.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								packages/redux-slider-monitor/examples/todomvc/constants/TodoFilters.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export const SHOW_ALL = 'show_all';
 | 
			
		||||
export const SHOW_MARKED = 'show_marked';
 | 
			
		||||
export const SHOW_UNMARKED = 'show_unmarked';
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import { createDevTools } from 'redux-devtools';
 | 
			
		||||
import DockMonitor from 'redux-devtools-dock-monitor';
 | 
			
		||||
import SliderMonitor from 'redux-slider-monitor';  // eslint-disable-line
 | 
			
		||||
 | 
			
		||||
export default createDevTools(
 | 
			
		||||
  <DockMonitor
 | 
			
		||||
    toggleVisibilityKey='ctrl-h'
 | 
			
		||||
    changePositionKey='ctrl-q'
 | 
			
		||||
    defaultPosition='bottom'
 | 
			
		||||
    defaultSize={0.15}
 | 
			
		||||
  >
 | 
			
		||||
    <SliderMonitor keyboardEnabled />
 | 
			
		||||
  </DockMonitor>
 | 
			
		||||
);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Provider } from 'react-redux';
 | 
			
		||||
import TodoApp from './TodoApp';
 | 
			
		||||
import DevTools from './DevTools';
 | 
			
		||||
 | 
			
		||||
const Root = ({ store }) => (
 | 
			
		||||
  <Provider store={store}>
 | 
			
		||||
    <div>
 | 
			
		||||
      <TodoApp />
 | 
			
		||||
      <DevTools />
 | 
			
		||||
    </div>
 | 
			
		||||
  </Provider>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Root.propTypes = {
 | 
			
		||||
  store: PropTypes.object.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Root;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
/* eslint-disable global-require */
 | 
			
		||||
if (process.env.NODE_ENV === 'production') {
 | 
			
		||||
  module.exports = require('./Root.prod');
 | 
			
		||||
} else {
 | 
			
		||||
  module.exports = require('./Root.dev');
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Provider } from 'react-redux';
 | 
			
		||||
import TodoApp from './TodoApp';
 | 
			
		||||
 | 
			
		||||
const Root = ({ store }) => (
 | 
			
		||||
  <Provider store={store}>
 | 
			
		||||
    <div>
 | 
			
		||||
      <TodoApp />
 | 
			
		||||
    </div>
 | 
			
		||||
  </Provider>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
Root.propTypes = {
 | 
			
		||||
  store: PropTypes.object.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Root;
 | 
			
		||||
							
								
								
									
										33
									
								
								packages/redux-slider-monitor/examples/todomvc/containers/TodoApp.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								packages/redux-slider-monitor/examples/todomvc/containers/TodoApp.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import { bindActionCreators } from 'redux';
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
import MainSection from '../components/MainSection';
 | 
			
		||||
import * as TodoActions from '../actions/TodoActions';
 | 
			
		||||
 | 
			
		||||
const TodoApp = ({ todos, actions }) => (
 | 
			
		||||
  <div>
 | 
			
		||||
    <Header addTodo={actions.addTodo} />
 | 
			
		||||
    <MainSection todos={todos} actions={actions} />
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
TodoApp.propTypes = {
 | 
			
		||||
  todos: PropTypes.array.isRequired,
 | 
			
		||||
  actions: PropTypes.object.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function mapState(state) {
 | 
			
		||||
  return {
 | 
			
		||||
    todos: state.todos
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mapDispatch(dispatch) {
 | 
			
		||||
  return {
 | 
			
		||||
    actions: bindActionCreators(TodoActions, dispatch)
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(mapState, mapDispatch)(TodoApp);
 | 
			
		||||
							
								
								
									
										23
									
								
								packages/redux-slider-monitor/examples/todomvc/index.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										23
									
								
								packages/redux-slider-monitor/examples/todomvc/index.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import 'todomvc-app-css/index.css';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import ReactDOM from 'react-dom';
 | 
			
		||||
import { AppContainer } from 'react-hot-loader';
 | 
			
		||||
import configureStore from './store/configureStore';
 | 
			
		||||
import Root from './containers/Root';
 | 
			
		||||
 | 
			
		||||
const store = configureStore();
 | 
			
		||||
 | 
			
		||||
const rootEl = document.getElementById('root');
 | 
			
		||||
const render = () => {
 | 
			
		||||
  ReactDOM.render(
 | 
			
		||||
    <AppContainer>
 | 
			
		||||
      <Root store={store} />
 | 
			
		||||
    </AppContainer>,
 | 
			
		||||
    rootEl
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
render(Root);
 | 
			
		||||
if (module.hot) {
 | 
			
		||||
  module.hot.accept('./containers/Root', render);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								packages/redux-slider-monitor/examples/todomvc/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/redux-slider-monitor/examples/todomvc/package.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "todomvc",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "description": "TodoMVC example for redux",
 | 
			
		||||
  "main": "server.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "../../node_modules/.bin/webpack-dev-server",
 | 
			
		||||
    "build": "../../node_modules/.bin/webpack --config webpack.config.prod.js"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://github.com/calesce/redux-slider-monitor.git"
 | 
			
		||||
  },
 | 
			
		||||
  "license": "MIT"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/redux-slider-monitor/examples/todomvc/reducers/index.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								packages/redux-slider-monitor/examples/todomvc/reducers/index.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import { combineReducers } from 'redux';
 | 
			
		||||
import todos from './todos';
 | 
			
		||||
 | 
			
		||||
const rootReducer = combineReducers({
 | 
			
		||||
  todos
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default rootReducer;
 | 
			
		||||
							
								
								
									
										50
									
								
								packages/redux-slider-monitor/examples/todomvc/reducers/todos.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										50
									
								
								packages/redux-slider-monitor/examples/todomvc/reducers/todos.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
import { ADD_TODO, DELETE_TODO, EDIT_TODO, MARK_TODO, MARK_ALL, CLEAR_MARKED } from '../constants/ActionTypes';
 | 
			
		||||
 | 
			
		||||
const initialState = [{
 | 
			
		||||
  text: 'Use Redux',
 | 
			
		||||
  marked: false,
 | 
			
		||||
  id: 0
 | 
			
		||||
}];
 | 
			
		||||
 | 
			
		||||
export default function todos(state = initialState, action) {
 | 
			
		||||
  switch (action.type) {
 | 
			
		||||
    case ADD_TODO:
 | 
			
		||||
      return [{
 | 
			
		||||
        id: (state.length === 0) ? 0 : state[0].id + 1,
 | 
			
		||||
        marked: false,
 | 
			
		||||
        text: action.text
 | 
			
		||||
      }, ...state];
 | 
			
		||||
 | 
			
		||||
    case DELETE_TODO:
 | 
			
		||||
      return state.filter(todo =>
 | 
			
		||||
        todo.id !== action.id
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    case EDIT_TODO:
 | 
			
		||||
      return state.map(todo => (
 | 
			
		||||
        todo.id === action.id ?
 | 
			
		||||
          { ...todo, text: action.text } :
 | 
			
		||||
          todo
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    case MARK_TODO:
 | 
			
		||||
      return state.map(todo => (
 | 
			
		||||
        todo.id === action.id ?
 | 
			
		||||
          { ...todo, marked: !todo.marked } :
 | 
			
		||||
          todo
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    case MARK_ALL: {
 | 
			
		||||
      const areAllMarked = state.every(todo => todo.marked);
 | 
			
		||||
      return state.map(todo => ({
 | 
			
		||||
        ...todo,
 | 
			
		||||
        marked: !areAllMarked
 | 
			
		||||
      }));
 | 
			
		||||
    }
 | 
			
		||||
    case CLEAR_MARKED:
 | 
			
		||||
      return state.filter(todo => todo.marked === false);
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return state;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import { createStore, compose } from 'redux';
 | 
			
		||||
import { persistState } from 'redux-devtools';
 | 
			
		||||
import rootReducer from '../reducers';
 | 
			
		||||
import DevTools from '../containers/DevTools';
 | 
			
		||||
 | 
			
		||||
const finalCreateStore = compose(
 | 
			
		||||
  DevTools.instrument(),
 | 
			
		||||
  persistState(
 | 
			
		||||
    window.location.href.match(
 | 
			
		||||
      /[?&]debug_session=([^&]+)\b/
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
)(createStore);
 | 
			
		||||
 | 
			
		||||
export default function configureStore(initialState) {
 | 
			
		||||
  const store = finalCreateStore(rootReducer, initialState);
 | 
			
		||||
 | 
			
		||||
  if (module.hot) {
 | 
			
		||||
    module.hot.accept('../reducers', () => store.replaceReducer(rootReducer));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return store;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
/* eslint-disable global-require */
 | 
			
		||||
if (process.env.NODE_ENV === 'production') {
 | 
			
		||||
  module.exports = require('./configureStore.prod');
 | 
			
		||||
} else {
 | 
			
		||||
  module.exports = require('./configureStore.dev');
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
import { createStore } from 'redux';
 | 
			
		||||
import rootReducer from '../reducers';
 | 
			
		||||
 | 
			
		||||
export default function configureStore(initialState) {
 | 
			
		||||
  return createStore(rootReducer, initialState);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								packages/redux-slider-monitor/examples/todomvc/webpack.config.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										48
									
								
								packages/redux-slider-monitor/examples/todomvc/webpack.config.js
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
const path = require('path');
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  devtool: 'eval-cheap-module-source-map',
 | 
			
		||||
  devServer: {
 | 
			
		||||
    contentBase: path.join(__dirname, 'dist'),
 | 
			
		||||
    host: 'localhost',
 | 
			
		||||
    port: process.env.PORT || 3000,
 | 
			
		||||
    historyApiFallback: true,
 | 
			
		||||
    hot: true
 | 
			
		||||
  },
 | 
			
		||||
  entry: [
 | 
			
		||||
    'react-hot-loader/patch',
 | 
			
		||||
    'webpack-dev-server/client?http://localhost:3000',
 | 
			
		||||
    'webpack/hot/only-dev-server',
 | 
			
		||||
    './index'
 | 
			
		||||
  ],
 | 
			
		||||
  output: {
 | 
			
		||||
    path: path.join(__dirname, 'dist'),
 | 
			
		||||
    filename: 'bundle.js'
 | 
			
		||||
  },
 | 
			
		||||
  plugins: [new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin()],
 | 
			
		||||
  resolve: {
 | 
			
		||||
    alias: {
 | 
			
		||||
      'redux-slider-monitor': path.join(__dirname, '..', '..', 'src/SliderMonitor')
 | 
			
		||||
    },
 | 
			
		||||
    extensions: ['.js']
 | 
			
		||||
  },
 | 
			
		||||
  module: {
 | 
			
		||||
    rules: [
 | 
			
		||||
      {
 | 
			
		||||
        test: /\.js$/,
 | 
			
		||||
        use: ['babel-loader'],
 | 
			
		||||
        exclude: /node_modules/,
 | 
			
		||||
        include: [__dirname, path.join(__dirname, '../../src')]
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        test: /\.css?$/,
 | 
			
		||||
        use: ['style-loader', 'raw-loader'],
 | 
			
		||||
        include: [
 | 
			
		||||
          __dirname,
 | 
			
		||||
          path.join(__dirname, '../../../../node_modules/todomvc-app-css')
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
// NOTE: This config is used for deploy to gh-pages
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
const devConfig = require('./webpack.config');
 | 
			
		||||
 | 
			
		||||
devConfig.entry = './index';
 | 
			
		||||
devConfig.plugins = [
 | 
			
		||||
  new webpack.NoEmitOnErrorsPlugin()
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
module.exports = devConfig;
 | 
			
		||||
							
								
								
									
										60
									
								
								packages/redux-slider-monitor/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								packages/redux-slider-monitor/package.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "redux-slider-monitor",
 | 
			
		||||
  "version": "2.0.0-2",
 | 
			
		||||
  "description": "A custom monitor for replaying Redux actions that works similarly to a video player",
 | 
			
		||||
  "main": "lib/SliderMonitor.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "clean": "rimraf lib",
 | 
			
		||||
    "build": "babel src --out-dir lib",
 | 
			
		||||
    "lint": "eslint src examples",
 | 
			
		||||
    "prepublish": "npm run lint && npm run clean && npm run build"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "url": "https://github.com/reduxjs/redux-devtools"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "Cale Newman <newman.cale@gmail.com> (http://github.com/calesce)",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "bugs": {
 | 
			
		||||
    "url": "https://github.com/reduxjs/redux-devtools/issues"
 | 
			
		||||
  },
 | 
			
		||||
  "homepage": "https://github.com/reduxjs/redux-devtools",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "babel-cli": "^6.24.1",
 | 
			
		||||
    "babel-core": "^6.24.1",
 | 
			
		||||
    "babel-eslint": "^7.2.2",
 | 
			
		||||
    "babel-loader": "^6.4.1",
 | 
			
		||||
    "babel-preset-es2015": "^6.24.1",
 | 
			
		||||
    "babel-preset-react": "^6.24.1",
 | 
			
		||||
    "babel-preset-stage-0": "^6.24.1",
 | 
			
		||||
    "classnames": "^2.1.2",
 | 
			
		||||
    "eslint": "^5.0.0",
 | 
			
		||||
    "eslint-config-airbnb": "^14.1.0",
 | 
			
		||||
    "eslint-plugin-import": "^2.2.0",
 | 
			
		||||
    "eslint-plugin-jsx-a11y": "^4.0.0",
 | 
			
		||||
    "eslint-plugin-react": "^7.4.0",
 | 
			
		||||
    "raw-loader": "^0.5.1",
 | 
			
		||||
    "react": "^16.7.0",
 | 
			
		||||
    "react-dom": "^16.7.0",
 | 
			
		||||
    "react-redux": "^6.0.0",
 | 
			
		||||
    "react-hot-loader": "^3.0.0-beta.6",
 | 
			
		||||
    "redux": "^4.0.0",
 | 
			
		||||
    "redux-devtools-dock-monitor": "^1.0.1",
 | 
			
		||||
    "redux-devtools-log-monitor": "^1.0.1",
 | 
			
		||||
    "redux-devtools": "^3.5.0",
 | 
			
		||||
    "rimraf": "^2.3.4",
 | 
			
		||||
    "style-loader": "^0.16.1",
 | 
			
		||||
    "todomvc-app-css": "^2.0.1",
 | 
			
		||||
    "webpack": "^2.2.1",
 | 
			
		||||
    "webpack-dev-server": "^2.4.1"
 | 
			
		||||
  },
 | 
			
		||||
  "peerDependencies": {
 | 
			
		||||
    "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0",
 | 
			
		||||
    "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0-0",
 | 
			
		||||
    "redux-devtools": "^3.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "devui": "^1.0.0-3",
 | 
			
		||||
    "prop-types": "^15.5.8",
 | 
			
		||||
    "redux-devtools-themes": "^1.0.0"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								packages/redux-slider-monitor/src/SliderButton.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								packages/redux-slider-monitor/src/SliderButton.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
import React, { Component, PureComponent } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import Button from 'devui/lib/Button';
 | 
			
		||||
 | 
			
		||||
export default class SliderButton extends (PureComponent || Component) {
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    theme: PropTypes.object,
 | 
			
		||||
    type: PropTypes.string,
 | 
			
		||||
    disabled: PropTypes.bool,
 | 
			
		||||
    onClick: PropTypes.func
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  iconStyle() {
 | 
			
		||||
    return {
 | 
			
		||||
      cursor: 'hand',
 | 
			
		||||
      fill: this.props.theme.base06,
 | 
			
		||||
      width: '1.8rem',
 | 
			
		||||
      height: '1.8rem'
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderPlayButton() {
 | 
			
		||||
    return (
 | 
			
		||||
      <Button
 | 
			
		||||
        onClick={this.props.onClick}
 | 
			
		||||
        title='Play'
 | 
			
		||||
        size='small'
 | 
			
		||||
        disabled={this.props.disabled}
 | 
			
		||||
        theme={this.props.theme}
 | 
			
		||||
      >
 | 
			
		||||
        <svg viewBox='0 0 24 24' preserveAspectRatio='xMidYMid meet' style={this.iconStyle()}>
 | 
			
		||||
          <g>
 | 
			
		||||
            <path d='M8 5v14l11-7z' />
 | 
			
		||||
          </g>
 | 
			
		||||
        </svg>
 | 
			
		||||
      </Button>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderPauseButton = () => (
 | 
			
		||||
    <Button
 | 
			
		||||
      onClick={this.props.onClick}
 | 
			
		||||
      title='Pause'
 | 
			
		||||
      size='small'
 | 
			
		||||
      disabled={this.props.disabled}
 | 
			
		||||
      theme={this.props.theme}
 | 
			
		||||
    >
 | 
			
		||||
      <svg viewBox='0 0 24 24' preserveAspectRatio='xMidYMid meet' style={this.iconStyle()}>
 | 
			
		||||
        <g>
 | 
			
		||||
          <path d='M6 19h4V5H6v14zm8-14v14h4V5h-4z' />
 | 
			
		||||
        </g>
 | 
			
		||||
      </svg>
 | 
			
		||||
    </Button>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  renderStepButton = (direction) => {
 | 
			
		||||
    const isLeft = direction === 'left';
 | 
			
		||||
    const d = isLeft
 | 
			
		||||
      ? 'M15.41 16.09l-4.58-4.59 4.58-4.59-1.41-1.41-6 6 6 6z'
 | 
			
		||||
      : 'M8.59 16.34l4.58-4.59-4.58-4.59 1.41-1.41 6 6-6 6z';
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Button
 | 
			
		||||
        size='small'
 | 
			
		||||
        title={isLeft ? 'Go back' : 'Go forward'}
 | 
			
		||||
        onClick={this.props.onClick}
 | 
			
		||||
        disabled={this.props.disabled}
 | 
			
		||||
        theme={this.props.theme}
 | 
			
		||||
      >
 | 
			
		||||
        <svg viewBox='0 0 24 24' preserveAspectRatio='xMidYMid meet' style={this.iconStyle()}>
 | 
			
		||||
          <g>
 | 
			
		||||
            <path d={d} />
 | 
			
		||||
          </g>
 | 
			
		||||
        </svg>
 | 
			
		||||
      </Button>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    switch (this.props.type) {
 | 
			
		||||
      case 'play':
 | 
			
		||||
        return this.renderPlayButton();
 | 
			
		||||
      case 'pause':
 | 
			
		||||
        return this.renderPauseButton();
 | 
			
		||||
      case 'stepLeft':
 | 
			
		||||
        return this.renderStepButton('left');
 | 
			
		||||
      case 'stepRight':
 | 
			
		||||
        return this.renderStepButton('right');
 | 
			
		||||
      default:
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										311
									
								
								packages/redux-slider-monitor/src/SliderMonitor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								packages/redux-slider-monitor/src/SliderMonitor.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,311 @@
 | 
			
		|||
import React, { Component, PureComponent } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import * as themes from 'redux-devtools-themes';
 | 
			
		||||
import { ActionCreators } from 'redux-devtools';
 | 
			
		||||
import { Toolbar, Divider } from 'devui/lib/Toolbar';
 | 
			
		||||
import Slider from 'devui/lib/Slider';
 | 
			
		||||
import Button from 'devui/lib/Button';
 | 
			
		||||
import SegmentedControl from 'devui/lib/SegmentedControl';
 | 
			
		||||
 | 
			
		||||
import reducer from './reducers';
 | 
			
		||||
import SliderButton from './SliderButton';
 | 
			
		||||
 | 
			
		||||
const { reset, jumpToState } = ActionCreators;
 | 
			
		||||
 | 
			
		||||
export default class SliderMonitor extends (PureComponent || Component) {
 | 
			
		||||
  static update = reducer;
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    dispatch: PropTypes.func,
 | 
			
		||||
    computedStates: PropTypes.array,
 | 
			
		||||
    stagedActionIds: PropTypes.array,
 | 
			
		||||
    actionsById: PropTypes.object,
 | 
			
		||||
    currentStateIndex: PropTypes.number,
 | 
			
		||||
    monitorState: PropTypes.shape({
 | 
			
		||||
      initialScrollTop: PropTypes.number
 | 
			
		||||
    }),
 | 
			
		||||
    preserveScrollTop: PropTypes.bool,
 | 
			
		||||
    stagedActions: PropTypes.array,
 | 
			
		||||
    select: PropTypes.func.isRequired,
 | 
			
		||||
    hideResetButton: PropTypes.bool,
 | 
			
		||||
    theme: PropTypes.oneOfType([
 | 
			
		||||
      PropTypes.object,
 | 
			
		||||
      PropTypes.string
 | 
			
		||||
    ]),
 | 
			
		||||
    keyboardEnabled: PropTypes.bool
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static defaultProps = {
 | 
			
		||||
    select: state => state,
 | 
			
		||||
    theme: 'nicinabox',
 | 
			
		||||
    preserveScrollTop: true,
 | 
			
		||||
    keyboardEnabled: true
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  state = {
 | 
			
		||||
    timer: undefined,
 | 
			
		||||
    replaySpeed: '1x'
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    if (typeof window !== 'undefined') {
 | 
			
		||||
      window.addEventListener('keydown', this.handleKeyPress);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillUnmount() {
 | 
			
		||||
    if (typeof window !== 'undefined') {
 | 
			
		||||
      window.removeEventListener('keydown', this.handleKeyPress);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setUpTheme = () => {
 | 
			
		||||
    let theme;
 | 
			
		||||
    if (typeof this.props.theme === 'string') {
 | 
			
		||||
      if (typeof themes[this.props.theme] !== 'undefined') {
 | 
			
		||||
        theme = themes[this.props.theme];
 | 
			
		||||
      } else {
 | 
			
		||||
        theme = themes.nicinabox;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      theme = this.props.theme;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return theme;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleReset = () => {
 | 
			
		||||
    this.pauseReplay();
 | 
			
		||||
    this.props.dispatch(reset());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleKeyPress = (event) => {
 | 
			
		||||
    if (!this.props.keyboardEnabled) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    if (event.ctrlKey && event.keyCode === 74) { // ctrl+j
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
 | 
			
		||||
      if (this.state.timer) {
 | 
			
		||||
        return this.pauseReplay();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this.state.replaySpeed === 'Live') {
 | 
			
		||||
        this.startRealtimeReplay();
 | 
			
		||||
      } else {
 | 
			
		||||
        this.startReplay();
 | 
			
		||||
      }
 | 
			
		||||
    } else if (event.ctrlKey && event.keyCode === 219) { // ctrl+[
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      this.stepLeft();
 | 
			
		||||
    } else if (event.ctrlKey && event.keyCode === 221) { // ctrl+]
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      this.stepRight();
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleSliderChange = (value) => {
 | 
			
		||||
    if (this.state.timer) {
 | 
			
		||||
      this.pauseReplay();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.props.dispatch(jumpToState(value));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startReplay = () => {
 | 
			
		||||
    const { computedStates, currentStateIndex, dispatch } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (computedStates.length < 2) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const speed = this.state.replaySpeed === '1x' ? 500 : 200;
 | 
			
		||||
 | 
			
		||||
    let stateIndex;
 | 
			
		||||
    if (currentStateIndex === computedStates.length - 1) {
 | 
			
		||||
      dispatch(jumpToState(0));
 | 
			
		||||
      stateIndex = 0;
 | 
			
		||||
    } else if (currentStateIndex === computedStates.length - 2) {
 | 
			
		||||
      dispatch(jumpToState(currentStateIndex + 1));
 | 
			
		||||
      return;
 | 
			
		||||
    } else {
 | 
			
		||||
      stateIndex = currentStateIndex + 1;
 | 
			
		||||
      dispatch(jumpToState(currentStateIndex + 1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let counter = stateIndex;
 | 
			
		||||
    const timer = setInterval(() => {
 | 
			
		||||
      if (counter + 1 <= computedStates.length - 1) {
 | 
			
		||||
        dispatch(jumpToState(counter + 1));
 | 
			
		||||
      }
 | 
			
		||||
      counter += 1;
 | 
			
		||||
 | 
			
		||||
      if (counter >= computedStates.length - 1) {
 | 
			
		||||
        clearInterval(this.state.timer);
 | 
			
		||||
        this.setState({
 | 
			
		||||
          timer: undefined
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }, speed);
 | 
			
		||||
 | 
			
		||||
    this.setState({ timer });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  startRealtimeReplay = () => {
 | 
			
		||||
    if (this.props.computedStates.length < 2) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.props.currentStateIndex === this.props.computedStates.length - 1) {
 | 
			
		||||
      this.props.dispatch(jumpToState(0));
 | 
			
		||||
 | 
			
		||||
      this.loop(0);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.loop(this.props.currentStateIndex);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loop = (index) => {
 | 
			
		||||
    let currentTimestamp = Date.now();
 | 
			
		||||
    let timestampDiff = this.getLatestTimestampDiff(index);
 | 
			
		||||
 | 
			
		||||
    const aLoop = () => {
 | 
			
		||||
      const replayDiff = Date.now() - currentTimestamp;
 | 
			
		||||
      if (replayDiff >= timestampDiff) {
 | 
			
		||||
        this.props.dispatch(jumpToState(this.props.currentStateIndex + 1));
 | 
			
		||||
 | 
			
		||||
        if (this.props.currentStateIndex >= this.props.computedStates.length - 1) {
 | 
			
		||||
          this.pauseReplay();
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        timestampDiff = this.getLatestTimestampDiff(this.props.currentStateIndex);
 | 
			
		||||
        currentTimestamp = Date.now();
 | 
			
		||||
 | 
			
		||||
        this.setState({
 | 
			
		||||
          timer: requestAnimationFrame(aLoop)
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        this.setState({
 | 
			
		||||
          timer: requestAnimationFrame(aLoop)
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (index !== this.props.computedStates.length - 1) {
 | 
			
		||||
      this.setState({
 | 
			
		||||
        timer: requestAnimationFrame(aLoop)
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getLatestTimestampDiff = index =>
 | 
			
		||||
    this.getTimestampOfStateIndex(index + 1) - this.getTimestampOfStateIndex(index)
 | 
			
		||||
 | 
			
		||||
  getTimestampOfStateIndex = (stateIndex) => {
 | 
			
		||||
    const id = this.props.stagedActionIds[stateIndex];
 | 
			
		||||
    return this.props.actionsById[id].timestamp;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pauseReplay = (cb) => {
 | 
			
		||||
    if (this.state.timer) {
 | 
			
		||||
      cancelAnimationFrame(this.state.timer);
 | 
			
		||||
      clearInterval(this.state.timer);
 | 
			
		||||
      this.setState({
 | 
			
		||||
        timer: undefined
 | 
			
		||||
      }, () => {
 | 
			
		||||
        if (typeof cb === 'function') {
 | 
			
		||||
          cb();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stepLeft = () => {
 | 
			
		||||
    this.pauseReplay();
 | 
			
		||||
 | 
			
		||||
    if (this.props.currentStateIndex !== 0) {
 | 
			
		||||
      this.props.dispatch(jumpToState(this.props.currentStateIndex - 1));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stepRight = () => {
 | 
			
		||||
    this.pauseReplay();
 | 
			
		||||
 | 
			
		||||
    if (this.props.currentStateIndex !== this.props.computedStates.length - 1) {
 | 
			
		||||
      this.props.dispatch(jumpToState(this.props.currentStateIndex + 1));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  changeReplaySpeed = (replaySpeed) => {
 | 
			
		||||
    this.setState({ replaySpeed });
 | 
			
		||||
 | 
			
		||||
    if (this.state.timer) {
 | 
			
		||||
      this.pauseReplay(() => {
 | 
			
		||||
        if (replaySpeed === 'Live') {
 | 
			
		||||
          this.startRealtimeReplay();
 | 
			
		||||
        } else {
 | 
			
		||||
          this.startReplay();
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      currentStateIndex, computedStates, actionsById, stagedActionIds, hideResetButton
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    const { replaySpeed } = this.state;
 | 
			
		||||
    const theme = this.setUpTheme();
 | 
			
		||||
 | 
			
		||||
    const max = computedStates.length - 1;
 | 
			
		||||
    const actionId = stagedActionIds[currentStateIndex];
 | 
			
		||||
    let actionType = actionsById[actionId].action.type;
 | 
			
		||||
    if (actionType === undefined) actionType = '<UNDEFINED>';
 | 
			
		||||
    else if (actionType === null) actionType = '<NULL>';
 | 
			
		||||
    else actionType = actionType.toString() || '<EMPTY>';
 | 
			
		||||
 | 
			
		||||
    const onPlayClick = replaySpeed === 'Live' ? this.startRealtimeReplay : this.startReplay;
 | 
			
		||||
    const playPause = this.state.timer ?
 | 
			
		||||
      <SliderButton theme={theme} type='pause' onClick={this.pauseReplay} /> :
 | 
			
		||||
      <SliderButton theme={theme} type='play' disabled={max <= 0} onClick={onPlayClick} />;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Toolbar noBorder compact fullHeight theme={theme}>
 | 
			
		||||
        {playPause}
 | 
			
		||||
        <Slider
 | 
			
		||||
          label={actionType}
 | 
			
		||||
          sublabel={`(${actionId})`}
 | 
			
		||||
          min={0}
 | 
			
		||||
          max={max}
 | 
			
		||||
          value={currentStateIndex}
 | 
			
		||||
          onChange={this.handleSliderChange}
 | 
			
		||||
          theme={theme}
 | 
			
		||||
        />
 | 
			
		||||
        <SliderButton
 | 
			
		||||
          theme={theme}
 | 
			
		||||
          type='stepLeft'
 | 
			
		||||
          disabled={currentStateIndex <= 0}
 | 
			
		||||
          onClick={this.stepLeft}
 | 
			
		||||
        />
 | 
			
		||||
        <SliderButton
 | 
			
		||||
          theme={theme}
 | 
			
		||||
          type='stepRight'
 | 
			
		||||
          disabled={currentStateIndex === max}
 | 
			
		||||
          onClick={this.stepRight}
 | 
			
		||||
        />
 | 
			
		||||
        <Divider theme={theme} />
 | 
			
		||||
        <SegmentedControl
 | 
			
		||||
          theme={theme}
 | 
			
		||||
          values={['Live', '1x', '2x']}
 | 
			
		||||
          selected={replaySpeed}
 | 
			
		||||
          onClick={this.changeReplaySpeed}
 | 
			
		||||
        />
 | 
			
		||||
        {!hideResetButton && [
 | 
			
		||||
          <Divider key='divider' theme={theme} />,
 | 
			
		||||
          <Button key='reset' theme={theme} onClick={this.handleReset}>Reset</Button>
 | 
			
		||||
        ]}
 | 
			
		||||
      </Toolbar>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/redux-slider-monitor/src/reducers.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/redux-slider-monitor/src/reducers.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export default function reducer() {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user