mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-01-31 11:51:41 +03:00
feat(redux-devtools-slider-monitor): convert example to TypeScript (#632)
This commit is contained in:
parent
89917320e5
commit
ec75d3a4b6
|
@ -11,6 +11,7 @@
|
||||||
"@types/node": "^14.6.0",
|
"@types/node": "^14.6.0",
|
||||||
"@types/webpack": "^4.41.21",
|
"@types/webpack": "^4.41.21",
|
||||||
"@types/webpack-dev-server": "^3.11.0",
|
"@types/webpack-dev-server": "^3.11.0",
|
||||||
|
"@types/webpack-env": "^1.15.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.9.0",
|
"@typescript-eslint/eslint-plugin": "^3.9.0",
|
||||||
"@typescript-eslint/parser": "^3.9.0",
|
"@typescript-eslint/parser": "^3.9.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
{
|
{
|
||||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-react",
|
||||||
|
"@babel/preset-typescript"
|
||||||
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-proposal-class-properties",
|
"@babel/plugin-proposal-class-properties",
|
||||||
"react-hot-loader/babel"
|
"react-hot-loader/babel"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: '../../../../.eslintrc',
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts', '*.tsx'],
|
||||||
|
extends: '../../../../eslintrc.ts.react.base.json',
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['webpack.config.ts'],
|
||||||
|
extends: '../../../../eslintrc.ts.base.json',
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.webpack.json'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -1,42 +0,0 @@
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import * as types from '../constants/ActionTypes';
|
||||||
|
|
||||||
|
interface AddTodoAction {
|
||||||
|
type: typeof types.ADD_TODO;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
export function addTodo(text: string): AddTodoAction {
|
||||||
|
return {
|
||||||
|
type: types.ADD_TODO,
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteTodoAction {
|
||||||
|
type: typeof types.DELETE_TODO;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
export function deleteTodo(id: number): DeleteTodoAction {
|
||||||
|
return {
|
||||||
|
type: types.DELETE_TODO,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditTodoAction {
|
||||||
|
type: typeof types.EDIT_TODO;
|
||||||
|
id: number;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
export function editTodo(id: number, text: string): EditTodoAction {
|
||||||
|
return {
|
||||||
|
type: types.EDIT_TODO,
|
||||||
|
id,
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarkTodoAction {
|
||||||
|
type: typeof types.MARK_TODO;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
export function markTodo(id: number): MarkTodoAction {
|
||||||
|
return {
|
||||||
|
type: types.MARK_TODO,
|
||||||
|
id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarkAllAction {
|
||||||
|
type: typeof types.MARK_ALL;
|
||||||
|
}
|
||||||
|
export function markAll(): MarkAllAction {
|
||||||
|
return {
|
||||||
|
type: types.MARK_ALL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClearMarkedAction {
|
||||||
|
type: typeof types.CLEAR_MARKED;
|
||||||
|
}
|
||||||
|
export function clearMarked(): ClearMarkedAction {
|
||||||
|
return {
|
||||||
|
type: types.CLEAR_MARKED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TodoAction =
|
||||||
|
| AddTodoAction
|
||||||
|
| DeleteTodoAction
|
||||||
|
| EditTodoAction
|
||||||
|
| MarkTodoAction
|
||||||
|
| MarkAllAction
|
||||||
|
| ClearMarkedAction;
|
||||||
|
|
||||||
|
export interface TodoActions {
|
||||||
|
addTodo(text: string): AddTodoAction;
|
||||||
|
deleteTodo(id: number): DeleteTodoAction;
|
||||||
|
editTodo(id: number, text: string): EditTodoAction;
|
||||||
|
markTodo(id: number): MarkTodoAction;
|
||||||
|
markAll(): MarkAllAction;
|
||||||
|
clearMarked(): ClearMarkedAction;
|
||||||
|
}
|
|
@ -1,7 +1,12 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component, MouseEventHandler } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
|
import {
|
||||||
|
SHOW_ALL,
|
||||||
|
SHOW_MARKED,
|
||||||
|
SHOW_UNMARKED,
|
||||||
|
TodoFilter,
|
||||||
|
} from '../constants/TodoFilters';
|
||||||
|
|
||||||
const FILTER_TITLES = {
|
const FILTER_TITLES = {
|
||||||
[SHOW_ALL]: 'All',
|
[SHOW_ALL]: 'All',
|
||||||
|
@ -9,7 +14,15 @@ const FILTER_TITLES = {
|
||||||
[SHOW_MARKED]: 'Completed',
|
[SHOW_MARKED]: 'Completed',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Footer extends Component {
|
interface Props {
|
||||||
|
markedCount: number;
|
||||||
|
unmarkedCount: number;
|
||||||
|
filter: TodoFilter;
|
||||||
|
onClearMarked: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
onShow: (filter: TodoFilter) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Footer extends Component<Props> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
markedCount: PropTypes.number.isRequired,
|
markedCount: PropTypes.number.isRequired,
|
||||||
unmarkedCount: PropTypes.number.isRequired,
|
unmarkedCount: PropTypes.number.isRequired,
|
||||||
|
@ -23,7 +36,7 @@ export default class Footer extends Component {
|
||||||
<footer className="footer">
|
<footer className="footer">
|
||||||
{this.renderTodoCount()}
|
{this.renderTodoCount()}
|
||||||
<ul className="filters">
|
<ul className="filters">
|
||||||
{[SHOW_ALL, SHOW_UNMARKED, SHOW_MARKED].map((filter) => (
|
{([SHOW_ALL, SHOW_UNMARKED, SHOW_MARKED] as const).map((filter) => (
|
||||||
<li key={filter}>{this.renderFilterLink(filter)}</li>
|
<li key={filter}>{this.renderFilterLink(filter)}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -43,7 +56,7 @@ export default class Footer extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFilterLink(filter) {
|
renderFilterLink(filter: TodoFilter) {
|
||||||
const title = FILTER_TITLES[filter];
|
const title = FILTER_TITLES[filter];
|
||||||
const { filter: selectedFilter, onShow } = this.props;
|
const { filter: selectedFilter, onShow } = this.props;
|
||||||
|
|
|
@ -2,12 +2,16 @@ import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TodoTextInput from './TodoTextInput';
|
import TodoTextInput from './TodoTextInput';
|
||||||
|
|
||||||
export default class Header extends Component {
|
interface Props {
|
||||||
|
addTodo: (text: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Header extends Component<Props> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
addTodo: PropTypes.func.isRequired,
|
addTodo: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSave = (text) => {
|
handleSave = (text: string) => {
|
||||||
if (text.length !== 0) {
|
if (text.length !== 0) {
|
||||||
this.props.addTodo(text);
|
this.props.addTodo(text);
|
||||||
}
|
}
|
|
@ -1,38 +1,49 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component, MouseEventHandler } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TodoItem from './TodoItem';
|
import TodoItem from './TodoItem';
|
||||||
import Footer from './Footer';
|
import Footer from './Footer';
|
||||||
import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters';
|
import {
|
||||||
|
SHOW_ALL,
|
||||||
|
SHOW_MARKED,
|
||||||
|
SHOW_UNMARKED,
|
||||||
|
TodoFilter,
|
||||||
|
} from '../constants/TodoFilters';
|
||||||
|
import { Todo } from '../reducers/todos';
|
||||||
|
import { TodoActions } from '../actions/TodoActions';
|
||||||
|
|
||||||
const TODO_FILTERS = {
|
const TODO_FILTERS = {
|
||||||
[SHOW_ALL]: () => true,
|
[SHOW_ALL]: () => true,
|
||||||
[SHOW_UNMARKED]: (todo) => !todo.marked,
|
[SHOW_UNMARKED]: (todo: Todo) => !todo.marked,
|
||||||
[SHOW_MARKED]: (todo) => todo.marked,
|
[SHOW_MARKED]: (todo: Todo) => todo.marked,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class MainSection extends Component {
|
interface State {
|
||||||
|
filter: TodoFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
todos: Todo[];
|
||||||
|
actions: TodoActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MainSection extends Component<Props, State> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
todos: PropTypes.array.isRequired,
|
todos: PropTypes.array.isRequired,
|
||||||
actions: PropTypes.object.isRequired,
|
actions: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context) {
|
state: State = { filter: SHOW_ALL };
|
||||||
super(props, context);
|
|
||||||
this.handleClearMarked = this.handleClearMarked.bind(this);
|
|
||||||
this.handleShow = this.handleShow.bind(this);
|
|
||||||
this.state = { filter: SHOW_ALL };
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClearMarked() {
|
handleClearMarked: MouseEventHandler<HTMLButtonElement> = () => {
|
||||||
const atLeastOneMarked = this.props.todos.some((todo) => todo.marked);
|
const atLeastOneMarked = this.props.todos.some((todo) => todo.marked);
|
||||||
if (atLeastOneMarked) {
|
if (atLeastOneMarked) {
|
||||||
this.props.actions.clearMarked();
|
this.props.actions.clearMarked();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
handleShow(filter) {
|
handleShow = (filter: TodoFilter) => {
|
||||||
this.setState({ filter });
|
this.setState({ filter });
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { todos, actions } = this.props;
|
const { todos, actions } = this.props;
|
||||||
|
@ -57,7 +68,7 @@ export default class MainSection extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderToggleAll(markedCount) {
|
renderToggleAll(markedCount: number) {
|
||||||
const { todos, actions } = this.props;
|
const { todos, actions } = this.props;
|
||||||
if (todos.length > 0) {
|
if (todos.length > 0) {
|
||||||
return (
|
return (
|
||||||
|
@ -65,6 +76,7 @@ export default class MainSection extends Component {
|
||||||
className="toggle-all"
|
className="toggle-all"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={markedCount === todos.length}
|
checked={markedCount === todos.length}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
onChange={actions.markAll}
|
onChange={actions.markAll}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -72,7 +84,7 @@ export default class MainSection extends Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFooter(markedCount) {
|
renderFooter(markedCount: number) {
|
||||||
const { todos } = this.props;
|
const { todos } = this.props;
|
||||||
const { filter } = this.state;
|
const { filter } = this.state;
|
||||||
const unmarkedCount = todos.length - markedCount;
|
const unmarkedCount = todos.length - markedCount;
|
|
@ -2,8 +2,23 @@ import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import TodoTextInput from './TodoTextInput';
|
import TodoTextInput from './TodoTextInput';
|
||||||
|
import { Todo } from '../reducers/todos';
|
||||||
|
|
||||||
export default class TodoItem extends Component {
|
interface State {
|
||||||
|
editing: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
todo: Todo;
|
||||||
|
addTodo: (text: string) => void;
|
||||||
|
deleteTodo: (id: number) => void;
|
||||||
|
editTodo: (id: number, text: string) => void;
|
||||||
|
markTodo: (id: number) => void;
|
||||||
|
markAll: () => void;
|
||||||
|
clearMarked: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TodoItem extends Component<Props, State> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
todo: PropTypes.object.isRequired,
|
todo: PropTypes.object.isRequired,
|
||||||
editTodo: PropTypes.func.isRequired,
|
editTodo: PropTypes.func.isRequired,
|
||||||
|
@ -11,18 +26,15 @@ export default class TodoItem extends Component {
|
||||||
markTodo: PropTypes.func.isRequired,
|
markTodo: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context) {
|
state: State = {
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
editing: false,
|
editing: false,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
handleDoubleClick = () => {
|
handleDoubleClick = () => {
|
||||||
this.setState({ editing: true });
|
this.setState({ editing: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSave = (id, text) => {
|
handleSave = (id: number, text: string) => {
|
||||||
if (text.length === 0) {
|
if (text.length === 0) {
|
||||||
this.props.deleteTodo(id);
|
this.props.deleteTodo(id);
|
||||||
} else {
|
} else {
|
|
@ -1,8 +1,25 @@
|
||||||
import React, { Component } from 'react';
|
import React, {
|
||||||
|
ChangeEventHandler,
|
||||||
|
Component,
|
||||||
|
FocusEventHandler,
|
||||||
|
KeyboardEventHandler,
|
||||||
|
} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
export default class TodoTextInput extends Component {
|
interface State {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onSave: (text: string) => void;
|
||||||
|
text?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
editing?: boolean;
|
||||||
|
newTodo?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TodoTextInput extends Component<Props, State> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSave: PropTypes.func.isRequired,
|
onSave: PropTypes.func.isRequired,
|
||||||
text: PropTypes.string,
|
text: PropTypes.string,
|
||||||
|
@ -18,15 +35,12 @@ export default class TodoTextInput extends Component {
|
||||||
newTodo: false,
|
newTodo: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context) {
|
state = {
|
||||||
super(props, context);
|
|
||||||
this.state = {
|
|
||||||
text: this.props.text || '',
|
text: this.props.text || '',
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit = (e) => {
|
handleSubmit: KeyboardEventHandler<HTMLInputElement> = (e) => {
|
||||||
const text = e.target.value.trim();
|
const text = e.currentTarget.value.trim();
|
||||||
if (e.which === 13) {
|
if (e.which === 13) {
|
||||||
this.props.onSave(text);
|
this.props.onSave(text);
|
||||||
if (this.props.newTodo) {
|
if (this.props.newTodo) {
|
||||||
|
@ -35,11 +49,11 @@ export default class TodoTextInput extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChange = (e) => {
|
handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||||
this.setState({ text: e.target.value });
|
this.setState({ text: e.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleBlur = (e) => {
|
handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
|
||||||
if (!this.props.newTodo) {
|
if (!this.props.newTodo) {
|
||||||
this.props.onSave(e.target.value);
|
this.props.onSave(e.target.value);
|
||||||
}
|
}
|
|
@ -1,3 +1,8 @@
|
||||||
export const SHOW_ALL = 'show_all';
|
export const SHOW_ALL = 'show_all';
|
||||||
export const SHOW_MARKED = 'show_marked';
|
export const SHOW_MARKED = 'show_marked';
|
||||||
export const SHOW_UNMARKED = 'show_unmarked';
|
export const SHOW_UNMARKED = 'show_unmarked';
|
||||||
|
|
||||||
|
export type TodoFilter =
|
||||||
|
| typeof SHOW_ALL
|
||||||
|
| typeof SHOW_MARKED
|
||||||
|
| typeof SHOW_UNMARKED;
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createDevTools } from 'redux-devtools';
|
import { createDevTools } from 'redux-devtools';
|
||||||
import DockMonitor from 'redux-devtools-dock-monitor';
|
import DockMonitor from 'redux-devtools-dock-monitor';
|
||||||
import SliderMonitor from 'redux-devtools-slider-monitor'; // eslint-disable-line
|
import SliderMonitor from 'redux-devtools-slider-monitor';
|
||||||
|
|
||||||
export default createDevTools(
|
export default createDevTools(
|
||||||
<DockMonitor
|
<DockMonitor
|
|
@ -2,10 +2,17 @@ import { hot } from 'react-hot-loader/root';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
import { Store } from 'redux';
|
||||||
import TodoApp from './TodoApp';
|
import TodoApp from './TodoApp';
|
||||||
import DevTools from './DevTools';
|
import DevTools from './DevTools';
|
||||||
|
import { TodoState } from '../reducers';
|
||||||
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
|
|
||||||
const Root = ({ store }) => (
|
interface Props {
|
||||||
|
store: Store<TodoState, TodoAction>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root: React.FunctionComponent<Props> = ({ store }) => (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<div>
|
<div>
|
||||||
<TodoApp />
|
<TodoApp />
|
||||||
|
@ -15,7 +22,7 @@ const Root = ({ store }) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
Root.propTypes = {
|
Root.propTypes = {
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.any.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default hot(Root);
|
export default hot(Root);
|
|
@ -1,6 +0,0 @@
|
||||||
/* eslint-disable global-require */
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
module.exports = require('./Root.prod');
|
|
||||||
} else {
|
|
||||||
module.exports = require('./Root.dev');
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { hot } from 'react-hot-loader/root';
|
|
||||||
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 hot(Root);
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { hot } from 'react-hot-loader/root';
|
||||||
|
import React, { FunctionComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import TodoApp from './TodoApp';
|
||||||
|
import { Store } from 'redux';
|
||||||
|
import { TodoState } from '../reducers';
|
||||||
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
store: Store<TodoState, TodoAction>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Root: FunctionComponent<Props> = ({ store }) => (
|
||||||
|
<Provider store={store}>
|
||||||
|
<div>
|
||||||
|
<TodoApp />
|
||||||
|
</div>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
Root.propTypes = {
|
||||||
|
store: PropTypes.any.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default hot(Root);
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Store } from 'redux';
|
||||||
|
import { ComponentType } from 'react';
|
||||||
|
import { TodoState } from '../reducers';
|
||||||
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
store: Store<TodoState, TodoAction>;
|
||||||
|
}
|
||||||
|
const Root: ComponentType<Props> =
|
||||||
|
process.env.NODE_ENV === 'production'
|
||||||
|
? // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
require('./Root.prod').default
|
||||||
|
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
require('./Root.dev').default;
|
||||||
|
export default Root;
|
|
@ -1,33 +0,0 @@
|
||||||
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);
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React, { FunctionComponent } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
|
import Header from '../components/Header';
|
||||||
|
import MainSection from '../components/MainSection';
|
||||||
|
import * as TodoActions from '../actions/TodoActions';
|
||||||
|
import {
|
||||||
|
TodoAction,
|
||||||
|
TodoActions as TodoActionsType,
|
||||||
|
} from '../actions/TodoActions';
|
||||||
|
import { TodoState } from '../reducers';
|
||||||
|
import { Todo } from '../reducers/todos';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
todos: Todo[];
|
||||||
|
actions: TodoActionsType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TodoApp: FunctionComponent<Props> = ({ todos, actions }) => (
|
||||||
|
<div>
|
||||||
|
{/* eslint-disable-next-line @typescript-eslint/unbound-method */}
|
||||||
|
<Header addTodo={actions.addTodo} />
|
||||||
|
<MainSection todos={todos} actions={actions} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
TodoApp.propTypes = {
|
||||||
|
todos: PropTypes.array.isRequired,
|
||||||
|
actions: PropTypes.any.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapState(state: TodoState) {
|
||||||
|
return {
|
||||||
|
todos: state.todos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatch(dispatch: Dispatch<TodoAction>) {
|
||||||
|
return {
|
||||||
|
actions: bindActionCreators(TodoActions, dispatch),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapState, mapDispatch)(TodoApp);
|
|
@ -1,23 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
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');
|
||||||
|
ReactDOM.render(
|
||||||
|
<AppContainer>
|
||||||
|
<Root store={store} />
|
||||||
|
</AppContainer>,
|
||||||
|
rootEl
|
||||||
|
);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept('./containers/Root', () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const RootContainer = require('./containers/Root').default;
|
||||||
|
ReactDOM.render(
|
||||||
|
<AppContainer>
|
||||||
|
<RootContainer store={store} />
|
||||||
|
</AppContainer>,
|
||||||
|
rootEl
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,22 +2,23 @@
|
||||||
"name": "slider-todomvc",
|
"name": "slider-todomvc",
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"description": "TodoMVC example for redux",
|
"description": "TodoMVC example for redux",
|
||||||
"private": true,
|
"license": "MIT",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "webpack-dev-server",
|
|
||||||
"build": "webpack --config webpack.config.prod.js"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/calesce/redux-slider-monitor.git"
|
"url": "https://github.com/calesce/redux-slider-monitor.git"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"scripts": {
|
||||||
|
"start": "webpack-dev-server",
|
||||||
|
"lint": "eslint . --ext .ts,.tsx",
|
||||||
|
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"type-check:watch": "npm run type-check -- --watch"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"react-hot-loader": "^4.12.21",
|
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
"react-hot-loader": "^4.12.21",
|
||||||
"react-redux": "^7.2.1",
|
"react-redux": "^7.2.1",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-devtools": "^3.7.0",
|
"redux-devtools": "^3.7.0",
|
||||||
|
@ -25,5 +26,12 @@
|
||||||
"redux-devtools-log-monitor": "^2.1.0",
|
"redux-devtools-log-monitor": "^2.1.0",
|
||||||
"redux-devtools-slider-monitor": "^2.0.0-5",
|
"redux-devtools-slider-monitor": "^2.0.0-5",
|
||||||
"todomvc-app-css": "^2.3.0"
|
"todomvc-app-css": "^2.3.0"
|
||||||
}
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/classnames": "^2.2.10",
|
||||||
|
"@types/react": "^16.9.46",
|
||||||
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-redux": "^7.1.9"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
import todos from './todos';
|
import todos, { Todo } from './todos';
|
||||||
|
|
||||||
|
export interface TodoState {
|
||||||
|
todos: Todo[];
|
||||||
|
}
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
todos,
|
todos,
|
|
@ -6,8 +6,15 @@ import {
|
||||||
MARK_ALL,
|
MARK_ALL,
|
||||||
CLEAR_MARKED,
|
CLEAR_MARKED,
|
||||||
} from '../constants/ActionTypes';
|
} from '../constants/ActionTypes';
|
||||||
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
|
|
||||||
const initialState = [
|
export interface Todo {
|
||||||
|
text: string;
|
||||||
|
marked: boolean;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: Todo[] = [
|
||||||
{
|
{
|
||||||
text: 'Use Redux',
|
text: 'Use Redux',
|
||||||
marked: false,
|
marked: false,
|
||||||
|
@ -15,7 +22,7 @@ const initialState = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function todos(state = initialState, action) {
|
export default function todos(state = initialState, action: TodoAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ADD_TODO:
|
case ADD_TODO:
|
||||||
return [
|
return [
|
||||||
|
@ -48,7 +55,7 @@ export default function todos(state = initialState, action) {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
case CLEAR_MARKED:
|
case CLEAR_MARKED:
|
||||||
return state.filter((todo) => todo.marked === false);
|
return state.filter((todo) => !todo.marked);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
|
@ -1,19 +0,0 @@
|
||||||
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,26 @@
|
||||||
|
import { createStore, compose, PreloadedState } from 'redux';
|
||||||
|
import { persistState } from 'redux-devtools';
|
||||||
|
import rootReducer, { TodoState } from '../reducers';
|
||||||
|
import DevTools from '../containers/DevTools';
|
||||||
|
|
||||||
|
function getDebugSessionKey() {
|
||||||
|
const matches = /[?&]debug_session=([^&#]+)\b/.exec(window.location.href);
|
||||||
|
return matches && matches.length > 0 ? matches[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enhancer = compose(
|
||||||
|
DevTools.instrument(),
|
||||||
|
persistState(getDebugSessionKey())
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function configureStore(
|
||||||
|
initialState?: PreloadedState<TodoState>
|
||||||
|
) {
|
||||||
|
const store = createStore(rootReducer, initialState, enhancer);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept('../reducers', () => store.replaceReducer(rootReducer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
/* eslint-disable global-require */
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
module.exports = require('./configureStore.prod');
|
|
||||||
} else {
|
|
||||||
module.exports = require('./configureStore.dev');
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { createStore } from 'redux';
|
|
||||||
import rootReducer from '../reducers';
|
|
||||||
|
|
||||||
export default function configureStore(initialState) {
|
|
||||||
return createStore(rootReducer, initialState);
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { createStore, PreloadedState } from 'redux';
|
||||||
|
import rootReducer, { TodoState } from '../reducers';
|
||||||
|
|
||||||
|
export default function configureStore(
|
||||||
|
initialState?: PreloadedState<TodoState>
|
||||||
|
) {
|
||||||
|
return createStore(rootReducer, initialState);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { PreloadedState, Store } from 'redux';
|
||||||
|
import { TodoState } from '../reducers';
|
||||||
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
|
|
||||||
|
const configureStore: (
|
||||||
|
initialState?: PreloadedState<TodoState>
|
||||||
|
) => Store<TodoState, TodoAction> =
|
||||||
|
process.env.NODE_ENV === 'production'
|
||||||
|
? // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
require('./configureStore.prod').default
|
||||||
|
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
require('./configureStore.dev').default;
|
||||||
|
export default configureStore;
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../../tsconfig.react.base.json",
|
||||||
|
"include": ["."]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../../tsconfig.base.json",
|
||||||
|
"include": ["webpack.config.ts"]
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
// 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;
|
|
|
@ -1,15 +1,8 @@
|
||||||
const path = require('path');
|
import * as path from 'path';
|
||||||
const webpack = require('webpack');
|
import * as webpack from 'webpack';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
devtool: 'eval-source-map',
|
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
|
||||||
devServer: {
|
|
||||||
contentBase: path.join(__dirname, 'dist'),
|
|
||||||
host: 'localhost',
|
|
||||||
port: process.env.PORT || 3000,
|
|
||||||
historyApiFallback: true,
|
|
||||||
hot: true,
|
|
||||||
},
|
|
||||||
entry: [
|
entry: [
|
||||||
'webpack-dev-server/client?http://localhost:3000',
|
'webpack-dev-server/client?http://localhost:3000',
|
||||||
'webpack/hot/only-dev-server',
|
'webpack/hot/only-dev-server',
|
||||||
|
@ -19,14 +12,13 @@ module.exports = {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
filename: 'bundle.js',
|
filename: 'bundle.js',
|
||||||
},
|
},
|
||||||
plugins: [new webpack.HotModuleReplacementPlugin()],
|
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.(js|ts)x?$/,
|
||||||
use: ['babel-loader'],
|
use: ['babel-loader'],
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
include: [__dirname, path.join(__dirname, '../../src')],
|
include: __dirname,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css?$/,
|
test: /\.css?$/,
|
||||||
|
@ -42,4 +34,16 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||||
|
},
|
||||||
|
plugins: [new webpack.HotModuleReplacementPlugin()],
|
||||||
|
devServer: {
|
||||||
|
contentBase: path.join(__dirname, 'dist'),
|
||||||
|
host: 'localhost',
|
||||||
|
port: process.env.PORT || 3000,
|
||||||
|
historyApiFallback: true,
|
||||||
|
hot: true,
|
||||||
|
},
|
||||||
|
devtool: 'eval-source-map',
|
||||||
};
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
import { Store } from 'redux';
|
import { Store } from 'redux';
|
||||||
|
import { ComponentType } from 'react';
|
||||||
import { TodoState } from '../reducers';
|
import { TodoState } from '../reducers';
|
||||||
import { TodoAction } from '../actions/TodoActions';
|
import { TodoAction } from '../actions/TodoActions';
|
||||||
import { ComponentType } from 'react';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
store: Store<TodoState, TodoAction>;
|
store: Store<TodoState, TodoAction>;
|
||||||
|
|
|
@ -48,8 +48,7 @@
|
||||||
"@types/prop-types": "^15.7.3",
|
"@types/prop-types": "^15.7.3",
|
||||||
"@types/react": "^16.9.46",
|
"@types/react": "^16.9.46",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9"
|
||||||
"@types/webpack-env": "^1.15.2"
|
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user