Skip to content

Commit

Permalink
todomvc redux
Browse files Browse the repository at this point in the history
  • Loading branch information
domagojk committed Dec 23, 2016
1 parent 113d004 commit 9e34da6
Show file tree
Hide file tree
Showing 24 changed files with 32,762 additions and 85 deletions.
3 changes: 3 additions & 0 deletions examples/TodoMVC-Redux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# TodoMVC With Store Example in Recycle

[Open the app](https://recycle.js.org/examples/TodoMVC-Store-1)
50 changes: 50 additions & 0 deletions examples/TodoMVC-Redux/components/Todo/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {ENTER_KEY, ESC_KEY} from '../../utils'

export default function actions (sources) {
const toggleCheckbox = sources.DOM.select('.toggle')
const destroyIcon = sources.DOM.select('.destroy')
const editInput = sources.DOM.select('.edit')
const todoLabel = sources.DOM.select('label')

return [
destroyIcon
.events('click')
.mapToLatest(sources.props)
.map(props => ({ type: 'destroy', id: props.id })),

toggleCheckbox
.events('change')
.mapToLatest(sources.props)
.map(props => ({ type: 'toggle', id: props.id })),

todoLabel
.events('dblclick')
.mapTo({ type: 'startEdit' }),

editInput
.events('input')
.map(ev => ({ type: 'inputVal', value: ev.target.value })),

editInput
.events('keyup')
.filter(ev => ev.keyCode === ENTER_KEY)
.merge(editInput.events('blur', true))
.mapToLatest(sources.props, sources.state)
.map(({props, state}) => ({ type: 'titleChanged', id: props.id, title: state.inputVal })),

editInput
.events('keyup')
.filter(ev => ev.keyCode === ESC_KEY)
.map(() => ({ type: 'cancelEdit' })),

sources.actions
.filterByType('cancelEdit')
.mapToLatest(sources.props)
.map(props => ({ type: 'inputVal', value: props.title })),

sources.actions
.filterByType('startEdit')
.mapToLatest(sources.props)
.map(props => ({ type: 'inputVal', value: props.title }))
]
}
16 changes: 16 additions & 0 deletions examples/TodoMVC-Redux/components/Todo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import actions from './actions'
import view, {componentDidUpdate} from './view'
import reducers from './reducers'

export default function Todo () {
return {
initialState: {
editing: false,
inputVal: ''
},
actions,
reducers,
view,
componentDidUpdate
}
}
31 changes: 31 additions & 0 deletions examples/TodoMVC-Redux/components/Todo/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default function reducers (sources) {
return [
sources.actions
.filterByType('startEdit')
.reducer(state => {
state.editing = true
return state
}),

sources.actions
.filterByType('cancelEdit')
.reducer(state => {
state.editing = false
return state
}),

sources.actions
.filterByType('titleChanged')
.reducer(state => {
state.editing = false
return state
}),

sources.actions
.filterByType('inputVal')
.reducer((state, action) => {
state.inputVal = action.value
return state
})
]
}
23 changes: 23 additions & 0 deletions examples/TodoMVC-Redux/components/Todo/view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import classNames from 'classnames'
import React from 'react'

export default function view (props, state) {
return (
<li className={'todoRoot ' + classNames({ completed: props.completed, editing: state.editing })}>
<div className='view'>
<input className='toggle' type='checkbox' checked={props.completed} />
<label>{props.title}</label>
<button className='destroy' />
</div>
<input className='edit' type='text' value={state.inputVal} />
</li>
)
}

export function componentDidUpdate ({select, state, prevState}) {
if (!prevState.editing && state.editing) {
const node = select('input.edit')
node.focus()
node.select()
}
}
38 changes: 38 additions & 0 deletions examples/TodoMVC-Redux/components/TodoList/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {ENTER_KEY, ESC_KEY} from '../../utils'

export default function actions (sources) {
const newTodoInput = sources.DOM.select('.new-todo')
const toggleAll = sources.DOM.select('.toggle-all')
const clearCompleted = sources.DOM.select('.clear-completed')

return [
toggleAll
.events('click')
.mapTo({ type: 'toggleAll' }),

clearCompleted
.events('click')
.mapTo({ type: 'deleteCompleted' }),

newTodoInput
.events('keydown')
.filter(e => e.keyCode === ESC_KEY)
.mapTo({ type: 'inputVal', payload: '' }),

newTodoInput
.events('keydown')
.filter(e => e.keyCode === ENTER_KEY)
.map(e => e.target.value)
.filter(val => val.length > 0)
.map(val => ({ type: 'insertTodo', payload: val })),

newTodoInput
.events('input')
.map(e => e.target.value)
.map(val => ({ type: 'inputVal', payload: val })),

sources.actions
.filterByType('insertTodo')
.mapTo({ type: 'inputVal', payload: '' })
]
}
14 changes: 14 additions & 0 deletions examples/TodoMVC-Redux/components/TodoList/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import actions from './actions'
import view from './view'
import reducers from './reducers'

export default function TodoList () {
return {
initialState: {
inputVal: ''
},
actions,
reducers,
view
}
}
10 changes: 10 additions & 0 deletions examples/TodoMVC-Redux/components/TodoList/reducers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function reducers (sources) {
return [
sources.actions
.filterByType('inputVal')
.reducer((state, action) => {
state.inputVal = action.payload
return state
})
]
}
55 changes: 55 additions & 0 deletions examples/TodoMVC-Redux/components/TodoList/view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Todo from '../../containers/Todo'
import React from 'react'

export default function view (props, state) {
let completed = props.todos.filter(todo => todo.completed).length
let active = props.todos.length - completed

return (
<div className='todoapp'>
<header className='header'>
<h1>todos</h1>
<input
className='new-todo'
type='text'
value={state.inputVal}
placeholder='What needs to be done?'
name='newTodo' />
</header>

<section className='main' style={{ 'display': props.todos.length ? '' : 'none' }}>
<input className='toggle-all' type='checkbox' defaultChecked={active === 0} />
<ul className='todo-list'>
{
props.todos
.filter(todoProps => !(props.filter === 'active' && todoProps.completed))
.filter(todoProps => !(props.filter === 'completed' && !todoProps.completed))
.map(props => (
<Todo
id={props.id}
title={props.title}
completed={props.completed}
key={props.id}
/>
))
}
</ul>
</section>

<footer className='footer' style={{ 'display': props.todos.length ? '' : 'none' }}>
<span className='todo-count'>
<strong>{active}</strong>
<span>{' item' + (active !== 1 ? 's' : '') + ' left'}</span>
</span>
<ul className='filters'>
<li><a href='#/' className={(props.filter === '') ? 'selected' : ''}>All</a></li>
<li><a href='#/active' className={(props.filter === 'active') ? 'selected' : ''}>Active</a></li>
<li><a href='#/completed' className={(props.filter === 'completed') ? 'selected' : ''}>Completed</a></li>
</ul>
{completed > 0 ? (
<button className='clear-completed'>Clear completed ({completed}) </button>
) : ''}
</footer>
</div>
)
}
30 changes: 30 additions & 0 deletions examples/TodoMVC-Redux/containers/Todo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Todo from '../components/Todo/index'
import React from 'react'

export default function TodoContainer (props) {
return {
dispatch (childrenActions) {
return [
childrenActions
.filterByType('toggle')
.map(action => ({ type: 'toggleTodo', id: action.id })),

childrenActions
.filterByType('destroy')
.map(action => ({ type: 'deleteTodo', id: action.id })),

childrenActions
.filterByType('titleChanged')
.map(action => ({ type: 'editTodo', id: action.id, title: action.title }))
]
},

view (props, state) {
const todo = state.list.find(todo => todo.id === props.id)
if (!todo) {
return null
}
return <Todo title={todo.title} id={todo.id} completed={todo.completed} />
}
}
}
14 changes: 14 additions & 0 deletions examples/TodoMVC-Redux/containers/TodoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import TodoList from '../components/TodoList/index'
import React from 'react'

export default function TodoListContainer () {
return {
dispatch (childrenActions) {
return childrenActions
},

view (props, state) {
return <TodoList todos={state.list} filter={props.route.filter} />
}
}
}
11 changes: 11 additions & 0 deletions examples/TodoMVC-Redux/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en" data-framework="recycle">
<head>
<meta charset="utf-8">
<title>Recycle • TodoMVC</title>
</head>
<body>
<div id="app"></div>
<script src="../bundles/todomvcredux.js"></script>
</body>
</html>
32 changes: 32 additions & 0 deletions examples/TodoMVC-Redux/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, hashHistory } from 'react-router'
import 'todomvc-common/base.css'
import 'todomvc-app-css/index.css'
import Recycle from '../../src/index'
import TodoList from './containers/TodoList'
import recycleRedux from '../../src/plugins/redux'
import { updateLocalStorage, getFromLocalStorage } from './utils'
import { createStore } from 'redux'
import todos from './reducers/todos'

const store = createStore(todos, {
list: getFromLocalStorage()
})

const TodoListReact = Recycle({
root: TodoList,
plugins: [recycleRedux(store)]
})

store.subscribe(() => {
updateLocalStorage(store.getState())
})

ReactDOM.render((
<Router history={hashHistory}>
<Route path='/' filter='' component={TodoListReact} />
<Route path='/completed' filter='completed' component={TodoListReact} />
<Route path='/active' filter='active' component={TodoListReact} />
</Router>
), document.getElementById('app'))
Loading

0 comments on commit 9e34da6

Please sign in to comment.