mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-11-04 09:57:26 +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:
 | 
					  directories:
 | 
				
			||||||
    - "node_modules"
 | 
					    - "node_modules"
 | 
				
			||||||
script:
 | 
					script:
 | 
				
			||||||
  - npm run lint
 | 
					 | 
				
			||||||
  - npm run build:all
 | 
					  - npm run build:all
 | 
				
			||||||
 | 
					  - npm run lint
 | 
				
			||||||
  - npm test
 | 
					  - 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