├── .babelrc
├── .gitignore
├── README.md
├── dist
├── error.html
├── favicon.ico
└── index.html
├── package.json
├── server.js
├── src
├── error.html
├── index.html
└── scripts
│ ├── actions
│ └── index.js
│ ├── components
│ ├── Footer.js
│ ├── Link.js
│ ├── Todo.js
│ ├── TodoApp.js
│ └── TodoList.js
│ ├── containers
│ ├── AddTodo.js
│ ├── FilterLink.js
│ └── VisibleTodoList.js
│ ├── index.js
│ ├── reducers
│ ├── index.js
│ ├── todos.js
│ └── visibilityFilter.js
│ └── store
│ └── index.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0,
3 | "env": {
4 | "development": {
5 | "plugins": [
6 | "react-transform"
7 | ],
8 | "extra": {
9 | "react-transform": {
10 | "transforms": [{
11 | "transform": "react-transform-hmr",
12 | "imports": ["react"],
13 | "locals": ["module"]
14 | }]
15 | }
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile ~/.gitignore_global
6 |
7 | # Ignore test files.
8 | /spec
9 | webpack_spec.config.js
10 |
11 | # Ignore node modules.
12 | /node_modules
13 |
14 | # Ignore temporary files.
15 | .DS_Store
16 | *.log
17 | /log
18 | /tmp
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Todo
2 |
3 | ##Getting Started with Redux
4 | The code is from following the video series [Getting Started with Redux](https://egghead.io/series/getting-started-with-redux) on egghead.io.
5 |
6 | ##Credits
7 | Dan Abramov is the creator of Redux and the videos mentioned above.
8 |
--------------------------------------------------------------------------------
/dist/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Error Page
11 |
12 |
13 |
--------------------------------------------------------------------------------
/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badnorseman/todos-react-redux/8eceede7728b093b5e0345070913fd26375a2ef8/dist/favicon.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todo",
3 | "version": "0.0.1",
4 | "description": "Todo",
5 | "scripts": {
6 | "build": "./node_modules/webpack/bin/webpack.js",
7 | "pretest": "webpack --config webpack_spec.config.js",
8 | "start": "node server.js",
9 | "test": "./node_modules/jasmine/bin/jasmine.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/urbanvikingr/todo.git"
14 | },
15 | "dependencies": {
16 | "react": "^0.14.3",
17 | "react-dom": "^0.14.3",
18 | "react-redux": "^4.0.0",
19 | "redux": "^3.0.4"
20 | },
21 | "devDependencies": {
22 | "babel-core": "^5.8.22",
23 | "babel-loader": "^5.3.2",
24 | "babel-plugin-react-transform": "^1.1.1",
25 | "express": "^4.13.3",
26 | "jasmine": "^2.4.1",
27 | "jasmine-ajax": "^3.2.0",
28 | "react-hot-loader": "^1.2.8",
29 | "react-transform-hmr": "^1.0.1",
30 | "webpack": "^1.12.9",
31 | "webpack-dev-middleware": "^1.4.0",
32 | "webpack-hot-middleware": "^2.6.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var webpackDevMiddleware = require("webpack-dev-middleware");
3 | var webpackHotMiddleware = require("webpack-hot-middleware");
4 | var config = require("./webpack.config");
5 |
6 | var app = new (require("express"))();
7 | var port = 8080;
8 |
9 | var compiler = webpack(config);
10 | app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
11 | app.use(webpackHotMiddleware(compiler));
12 |
13 | app.get("/", function(req, res) {
14 | res.sendFile(__dirname + "/dist/index.html")
15 | });
16 |
17 | app.listen(port, function(error) {
18 | if (error) {
19 | console.error(error);
20 | } else {
21 | console.info("Listening on port %s. Open http://localhost:%s/ in browser", port, port);
22 | }
23 | });
24 |
--------------------------------------------------------------------------------
/src/error.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Error Page
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/scripts/actions/index.js:
--------------------------------------------------------------------------------
1 | let nextTodoId = 0;
2 | export const addTodo = (text) => {
3 | return {
4 | type: "ADD_TODO",
5 | id: nextTodoId++,
6 | text
7 | };
8 | };
9 |
10 | export const setVisibilityFilter = (filter) => {
11 | return {
12 | type: "SET_VISIBILITY_FILTER",
13 | filter
14 | };
15 | };
16 |
17 | export const toggleTodo = (id) => {
18 | return {
19 | type: "TOGGLE_TODO",
20 | id
21 | };
22 | };
23 |
--------------------------------------------------------------------------------
/src/scripts/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import FilterLink from "../containers/FilterLink";
3 |
4 | const Footer = () => (
5 |
6 | Show:
7 | {" "}
8 |
9 | All
10 |
11 | {", "}
12 |
13 | Active
14 |
15 | {", "}
16 |
17 | Completed
18 |
19 |
20 | );
21 |
22 | export default Footer
23 |
--------------------------------------------------------------------------------
/src/scripts/components/Link.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Link = ({
4 | active,
5 | children,
6 | onClick
7 | }) => {
8 | if (active) {
9 | return {children};
10 | }
11 |
12 | return (
13 | {
15 | e.preventDefault();
16 | onClick();
17 | }}
18 | >
19 | {children}
20 |
21 | );
22 | };
23 |
24 | export default Link
25 |
--------------------------------------------------------------------------------
/src/scripts/components/Todo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Todo = ({
4 | onClick,
5 | completed,
6 | text
7 | }) => (
8 |
17 | {text}
18 |
19 | );
20 |
21 | export default Todo
22 |
--------------------------------------------------------------------------------
/src/scripts/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import AddTodo from "../containers/AddTodo";
3 | import Footer from "./Footer";
4 | import VisibleTodoList from "../containers/VisibleTodoList";
5 |
6 | const TodoApp = () => (
7 |
12 | );
13 |
14 | export default TodoApp
15 |
--------------------------------------------------------------------------------
/src/scripts/components/TodoList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Todo from "./Todo";
3 |
4 | const TodoList = ({
5 | todos,
6 | onTodoClick
7 | }) => (
8 |
9 | {todos.map(todo =>
10 | onTodoClick(todo.id)}
14 | />
15 | )}
16 |
17 | );
18 |
19 | export default TodoList
20 |
--------------------------------------------------------------------------------
/src/scripts/containers/AddTodo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import { addTodo } from "../actions";
4 |
5 | let AddTodo = ({ dispatch }) => {
6 | let input;
7 |
8 | return (
9 |
10 | {
11 | input = node;
12 | }} />
13 |
19 |
20 | );
21 | };
22 | AddTodo = connect()(AddTodo);
23 |
24 | export default AddTodo
25 |
--------------------------------------------------------------------------------
/src/scripts/containers/FilterLink.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import { setVisibilityFilter } from "../actions";
4 | import Link from "../components/Link";
5 |
6 | const mapStateToProps = (
7 | state,
8 | ownProps
9 | ) => {
10 | return {
11 | active:
12 | ownProps.filter ===
13 | state.visibilityFilter
14 | };
15 | };
16 |
17 | const mapDispatchToProps = (
18 | dispatch,
19 | ownProps
20 | ) => {
21 | return {
22 | onClick: () => {
23 | dispatch(
24 | setVisibilityFilter(ownProps.filter)
25 | );
26 | }
27 | };
28 | }
29 |
30 | const FilterLink = connect(
31 | mapStateToProps,
32 | mapDispatchToProps
33 | )(Link);
34 |
35 | export default FilterLink
36 |
--------------------------------------------------------------------------------
/src/scripts/containers/VisibleTodoList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { connect } from "react-redux";
3 | import { toggleTodo } from "../actions";
4 | import TodoList from "../components/TodoList";
5 |
6 | const getVisibleTodos = (
7 | todos,
8 | filter
9 | ) => {
10 | switch (filter) {
11 | case "SHOW_ALL":
12 | return todos;
13 | case "SHOW_COMPLETED":
14 | return todos.filter(
15 | t => t.completed
16 | );
17 | case "SHOW_ACTIVE":
18 | return todos.filter(
19 | t => !t.completed
20 | );
21 | }
22 | }
23 |
24 | const mapStateToProps = (
25 | state
26 | ) => {
27 | return {
28 | todos: getVisibleTodos(
29 | state.todos,
30 | state.visibilityFilter
31 | )
32 | };
33 | };
34 |
35 | const mapDispatchToProps = (
36 | dispatch
37 | ) => {
38 | return {
39 | onTodoClick: (id) => {
40 | dispatch(toggleTodo(id));
41 | }
42 | };
43 | };
44 |
45 | const VisibleTodoList = connect(
46 | mapStateToProps,
47 | mapDispatchToProps
48 | )(TodoList);
49 |
50 | export default VisibleTodoList
51 |
--------------------------------------------------------------------------------
/src/scripts/index.js:
--------------------------------------------------------------------------------
1 | import "babel-core/polyfill";
2 | import React from "react";
3 | import { render } from "react-dom";
4 | import { Provider } from "react-redux";
5 | import { todoStore } from "./store";
6 | import TodoApp from "./components/TodoApp";
7 |
8 | render(
9 |
10 |
11 | ,
12 | document.getElementById("app")
13 | );
14 |
--------------------------------------------------------------------------------
/src/scripts/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 | import { todos } from "./todos";
3 | import { visibilityFilter } from "./visibilityFilter";
4 |
5 | export const todoApp = combineReducers({
6 | todos,
7 | visibilityFilter
8 | });
9 |
--------------------------------------------------------------------------------
/src/scripts/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const todo = (state, action) => {
2 | switch (action.type) {
3 | case "ADD_TODO":
4 | return {
5 | id: action.id,
6 | text: action.text,
7 | completed: false
8 | };
9 | case "TOGGLE_TODO":
10 | if (state.id !== action.id) {
11 | return state;
12 | }
13 |
14 | return {
15 | ...state,
16 | completed: !state.completed
17 | };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export const todos = (state = [], action) => {
24 | switch (action.type) {
25 | case "ADD_TODO":
26 | return [
27 | ...state,
28 | todo(undefined, action)
29 | ];
30 | case "TOGGLE_TODO":
31 | return state.map(t =>
32 | todo(t, action)
33 | );
34 | default:
35 | return state;
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/src/scripts/reducers/visibilityFilter.js:
--------------------------------------------------------------------------------
1 | export const visibilityFilter = (
2 | state = "SHOW_ALL",
3 | action
4 | ) => {
5 | switch (action.type) {
6 | case "SET_VISIBILITY_FILTER":
7 | return action.filter;
8 | default:
9 | return state;
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/src/scripts/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "redux";
2 | import { todoApp } from "../reducers";
3 |
4 | export const todoStore = createStore(todoApp);
5 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 | var webpack = require("webpack");
3 |
4 | module.exports = {
5 | devtool: "eval-source-map",
6 | entry: [
7 | "webpack-hot-middleware/client",
8 | "./src/scripts/index.js"
9 | ],
10 | output: {
11 | path: path.join(__dirname, "dist"),
12 | filename: "bundle.js",
13 | publicPath: "/"
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.HotModuleReplacementPlugin(),
18 | new webpack.NoErrorsPlugin()
19 | ],
20 | resolve: {
21 | extensions: ["", ".js"]
22 | },
23 | module: {
24 | loaders: [{
25 | test: /\.js$/,
26 | loader: "babel",
27 | exclude: /node_modules/,
28 | include: path.join(__dirname, "/src/scripts")
29 | }]
30 | }
31 | };
32 |
--------------------------------------------------------------------------------