Skip to content

Instantly share code, notes, and snippets.

@acestudiooleg
Forked from toranb/actions.todos.js
Last active February 1, 2018 17:51
Show Gist options
  • Save acestudiooleg/a98fc4f8faaad644f72338511bf86408 to your computer and use it in GitHub Desktop.
Save acestudiooleg/a98fc4f8faaad644f72338511bf86408 to your computer and use it in GitHub Desktop.
myadt
import * as types from '../actions/types';
export const addTodo = text => dispatch => dispatch({type: types.ADD_TODO, text});
export const deleteTodo = id => dispatch => dispatch({type: types.DELETE_TODO, id});
export const editTodo = (id, text) => dispatch => dispatch({type: types.EDIT_TODO, id, text});
export const completeTodo = id => dispatch => dispatch({type: types.COMPLETE_TODO, id});
export const clearCompleted = () => dispatch => dispatch({type: types.CLEAR_COMPLETED});
export const showAll = () => dispatch => dispatch({type: types.SHOW_ALL});
export const showActive = () => dispatch => dispatch({type: types.SHOW_ACTIVE});
export const showCompleted = () => dispatch => dispatch({type: types.SHOW_COMPLETED});
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
export const SHOW_ALL = 'SHOW_ALL';
export const SHOW_ACTIVE = 'SHOW_ACTIVE';
export const SHOW_COMPLETED = 'SHOW_COMPLETED';
import Ember from 'ember';
import { connect } from 'ember-redux';
import { editTodo, deleteTodo, completeTodo } from '../actions/todos';
const { get, set, run: { scheduleOnce } } = Ember;
const TodoItemComponent = Ember.Component.extend({
tagName: 'li',
editing: false,
classNameBindings: ['todo.completed', 'editing'],
actions: {
startEditing() {
set(this, 'editing', true);
},
doneEditing() {
set(this, 'editing', false);
},
focusInput() {
scheduleOnce('afterRender', this, () => {
this.element.querySelector('input.edit').focus();
});
},
handleKeydown(e) {
if (e.keyCode === 13) {
e.target.blur();
} else if (e.keyCode === 27) {
set(this, 'editing', false);
}
}
}
});
const dispatchToActions = {
deleteTodo,
completeTodo,
editTodo
}
export default connect(null, dispatchToActions)(TodoItemComponent);
import Ember from 'ember';
import { connect } from 'ember-redux';
import { getTodos } from '../reducers/todos';
const TodoListComponent = Ember.Component.extend({
tagName: 'ul',
classNames: 'todo-list'
});
const stateToComputed = state => {
return {
todos: getTodos(state)
};
};
export default connect(stateToComputed)(TodoListComponent);
import Ember from 'ember';
const { get, computed } = Ember;
export default Ember.Component.extend({
tagName: 'span',
classNames: 'todo-count',
itemWord: computed('todosCount', function() {
let count = get(this, 'todosCount');
return count > 1 ? 'items' : 'item';
})
});
import Ember from 'ember';
import { connect } from 'ember-redux';
import { addTodo } from '../actions/todos';
const HeaderComponent = Ember.Component.extend({
text: '',
tagName: 'header',
classNames: 'header'
});
const dispatchToActions = {
addTodo
}
export default connect(null, dispatchToActions)(HeaderComponent);
import Ember from 'ember';
export default Ember.Component.extend({
classNames: 'todoapp'
});
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'section',
classNames: 'main'
});
import Ember from 'ember';
export function gt([n1, n2]) {
return n1 > n2;
}
export default Ember.Helper.helper(gt);
import Ember from 'ember';
export default Ember.Helper.helper(function(params) {
return params[0] === params[1];
});
import Ember from 'ember';
export function invokeFunction(acc, curr) {
return curr(acc);
}
export function pipe(actions = []) {
return function(...args) {
return actions.reduce((acc, curr, idx) => {
if (idx === 0) {
return curr(...args);
}
return invokeFunction(acc, curr);
}, undefined);
};
}
export default Ember.Helper.helper(pipe);
import { combineReducers } from 'redux'
import todos from './todos';
const rootReducer = combineReducers({
todos
})
export default rootReducer
import _ from 'lodash';
import reselect from 'reselect';
import {
CLEAR_COMPLETED,
SHOW_ACTIVE,
SHOW_COMPLETED,
SHOW_ALL,
DELETE_TODO,
EDIT_TODO,
COMPLETE_TODO,
ADD_TODO
} from '../actions/types';
const { createSelector } = reselect;
const makeReducer = (reducer = () => []) => (state, action) => {
console.log(state, 1, action);
let newState = {...state};
const mutations = reducer(state);
mutations.forEach(([actionType, mutation]) => {
if (actionType === action.type) {
const a = mutation(action);
console.log(a);
newState = {...newState, ...a};
}
});
return newState;
};
const initialState = {
filter: null,
all: [{
id: 1,
text: 'Use Ember Redux',
completed: false
}]
};
const changeTodo = (state, propName, newValue) => payload => ({
all: state.all.map(todo => (todo.id === payload.id ? {...todo, [propName]: newValue || payload[propName]} : todo))
});
const todos = makeReducer(state => [
[CLEAR_COMPLETED, () => ({
all: state.all.filter(({completed}) => completed === true)
})],
[SHOW_ACTIVE, () => ({filter: false})],
[SHOW_COMPLETED, () => ({filter: true})],
[SHOW_ALL, () => ({filter: null})],
[DELETE_TODO, ({id}) => ({
all: state.all.filer(todo => todo.id !== id)
})],
[COMPLETE_TODO, changeTodo(state, 'completed', true)],
[EDIT_TODO, changeTodo(state, 'text')],
[ADD_TODO, ({text}) => {
const id = (all.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1);
const todo = { id, text, completed: false };
return [...state.all, [todo]]
}]
]);
console.log(todos);
export default todos;
const all = state => state.todos.all;
const filter = state => state.todos.filter;
export const getTodos = createSelector(
all,
filter,
(all, filter) => {
return _.omitBy(all, todo => {
return filter === undefined ? false : filter !== todo.completed;
});
}
);
export const getAllTodosCount = createSelector(all, (all) => Object.values(all).length);
export const getFilter = createSelector(filter, filter => filter);
export const getTodosCount = createSelector(getTodos, (todos) => Object.values(todos).length);
export const getCompletedCount = createSelector(all, (all) => Object.values(all).filter(t => t.completed).length);
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #f5f5f5;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
text-align: center;
border: none; /* Mobile Safari */
opacity: 0;
position: absolute;
}
.toggle-all + label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all + label:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked + label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle + label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
}
.todo-list li .toggle:checked + label {
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 60px;
display: block;
line-height: 1.2;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
margin-bottom: 11px;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
padding: 10px 15px;
height: 20px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
0 8px 0 -3px #f6f6f6,
0 9px 1px -3px rgba(0, 0, 0, 0.2),
0 16px 0 -6px #f6f6f6,
0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
<h1>todos</h1>
<form {{action (pipe (action props.addTodo props.text) (action (mut props.text) "")) on="submit"}}>
<input type="text" class="new-todo" placeholder="What needs to be done?" value={{props.text}} oninput={{action (mut props.text) value="target.value"}} autofocus>
<input style="display: none" type="submit" value="submit" />
</form>
{{yield (hash
deleteTodo=(action "deleteTodo" todo.id)
completeTodo=(action "completeTodo" todo.id)
editTodo=(action "editTodo" todo.id)
handleKeydown=(action "handleKeydown")
focusInput=(action "focusInput")
startEditing=(action "startEditing")
doneEditing=(action "doneEditing"))
}}
<div class="view">
<input type="checkbox" checked={{todo.completed}} class="toggle" onchange={{action props.completeTodo}}>
<label ondblclick={{action (pipe (action props.startEditing) (action props.focusInput))}}>{{todo.text}}</label>
<button class="destroy" onclick={{action props.deleteTodo}}></button>
</div>
<input type="text" class="edit" value={{todo.text}} onblur={{action (pipe (action props.editTodo value="target.value") (action props.doneEditing))}} onkeydown={{action props.handleKeydown}}>
{{yield (hash text=text addTodo=(action "addTodo"))}}
{{#ui-header as |props|}}
{{header-markup props=props}}
{{/ui-header}}
{{#ui-section}}
<input type="checkbox" class="toggle-all" value="on">
{{#todo-list as |todos|}}
{{#each-in todos as |key todo|}}
{{#todo-item todo=todo as |props|}}
{{todo-markup todo=todo props=props}}
{{/todo-item}}
{{/each-in}}
{{/todo-list}}
{{/ui-section}}
{{#ui-footer as |props|}}
{{footer-markup props=props}}
{{/ui-footer}}
import Ember from 'ember';
export default function destroyApp(application) {
Ember.run(application, 'destroy');
}
import Resolver from '../../resolver';
import config from '../../config/environment';
const resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
const { run } = Ember;
const assign = Ember.assign || Ember.merge;
export default function startApp(attrs) {
let application;
let attributes = assign({rootElement: "#test-root"}, config.APP);
attributes = assign(attributes, attrs); // use defaults, but you can override;
run(() => {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}
import resolver from './helpers/resolver';
import {
setResolver
} from 'ember-qunit';
setResolver(resolver);
{
"version": "0.12.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.12.0",
"ember-template-compiler": "2.12.0",
"ember-testing": "2.12.0"
},
"addons": {
"ember-redux": "3.0.0",
"ember-reselect-shim": "2.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment