mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
complete base implementation of todoapp
This commit is contained in:
@@ -2,6 +2,8 @@ import { TodoItem } from "./TodoItem.js";
|
|||||||
|
|
||||||
const { StoreMixin, Component } = odoo.core;
|
const { StoreMixin, Component } = odoo.core;
|
||||||
|
|
||||||
|
const ENTER_KEY = 13;
|
||||||
|
|
||||||
export class TodoApp extends StoreMixin(Component) {
|
export class TodoApp extends StoreMixin(Component) {
|
||||||
template = "todoapp";
|
template = "todoapp";
|
||||||
widgets = { TodoItem };
|
widgets = { TodoItem };
|
||||||
@@ -11,12 +13,23 @@ export class TodoApp extends StoreMixin(Component) {
|
|||||||
return this.env.store.state.todos;
|
return this.env.store.state.todos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get visibleTodos() {
|
||||||
|
let todos = this.todos;
|
||||||
|
if (this.state.filter === "active") {
|
||||||
|
todos = todos.filter(t => !t.completed);
|
||||||
|
}
|
||||||
|
if (this.state.filter === "completed") {
|
||||||
|
todos = todos.filter(t => t.completed);
|
||||||
|
}
|
||||||
|
return todos;
|
||||||
|
}
|
||||||
|
|
||||||
get allChecked() {
|
get allChecked() {
|
||||||
return this.todos.every(todo => todo.done);
|
return this.todos.every(todo => todo.completed);
|
||||||
}
|
}
|
||||||
|
|
||||||
get remaining() {
|
get remaining() {
|
||||||
return this.todos.filter(todo => !todo.done).length;
|
return this.todos.filter(todo => !todo.completed).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
get remainingText() {
|
get remainingText() {
|
||||||
@@ -24,10 +37,10 @@ export class TodoApp extends StoreMixin(Component) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addTodo(ev) {
|
addTodo(ev) {
|
||||||
if (ev.keyCode === 13) {
|
if (ev.keyCode === ENTER_KEY) {
|
||||||
const text = ev.target.value;
|
const title = ev.target.value;
|
||||||
if (text.trim()) {
|
if (title.trim()) {
|
||||||
this.env.store.dispatch("addTodo", text);
|
this.env.store.dispatch("addTodo", title);
|
||||||
}
|
}
|
||||||
ev.target.value = "";
|
ev.target.value = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
const ENTER_KEY = 13;
|
||||||
|
const ESC_KEY = 27;
|
||||||
|
|
||||||
export class TodoItem extends odoo.core.Component {
|
export class TodoItem extends odoo.core.Component {
|
||||||
template = "todoitem";
|
template = "todoitem";
|
||||||
|
|
||||||
|
state = { isEditing: false };
|
||||||
|
|
||||||
removeTodo() {
|
removeTodo() {
|
||||||
this.env.store.dispatch("removeTodo", this.props.id);
|
this.env.store.dispatch("removeTodo", this.props.id);
|
||||||
}
|
}
|
||||||
@@ -8,4 +13,37 @@ export class TodoItem extends odoo.core.Component {
|
|||||||
toggleTodo() {
|
toggleTodo() {
|
||||||
this.env.store.dispatch("toggleTodo", this.props.id);
|
this.env.store.dispatch("toggleTodo", this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async editTodo() {
|
||||||
|
await this.updateState({ isEditing: true });
|
||||||
|
this.refs.input.value = "";
|
||||||
|
this.refs.input.focus();
|
||||||
|
this.refs.input.value = this.props.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyup(ev) {
|
||||||
|
if (ev.keyCode === ENTER_KEY) {
|
||||||
|
this.updateTitle(ev.target.value);
|
||||||
|
}
|
||||||
|
if (ev.keyCode === ESC_KEY) {
|
||||||
|
ev.target.value = this.props.title;
|
||||||
|
this.updateState({ isEditing: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlur(ev) {
|
||||||
|
this.updateTitle(ev.target.value);
|
||||||
|
}
|
||||||
|
updateTitle(title) {
|
||||||
|
const value = title.trim();
|
||||||
|
if (!value) {
|
||||||
|
this.removeTodo(this.props.id);
|
||||||
|
} else {
|
||||||
|
this.env.store.dispatch("editTodo", {
|
||||||
|
id: this.props.id,
|
||||||
|
title: value
|
||||||
|
});
|
||||||
|
this.updateState({ isEditing: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-21
@@ -3,27 +3,30 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
addTodo({ commit }, text) {
|
addTodo({ commit }, title) {
|
||||||
commit("addTodo", text);
|
commit("addTodo", title);
|
||||||
},
|
},
|
||||||
removeTodo({ commit }, id) {
|
removeTodo({ commit }, id) {
|
||||||
commit("removeTodo", id);
|
commit("removeTodo", id);
|
||||||
},
|
},
|
||||||
toggleTodo({ state, commit }, id) {
|
toggleTodo({ state, commit }, id) {
|
||||||
const todo = state.todos.find(t => t.id === id);
|
const todo = state.todos.find(t => t.id === id);
|
||||||
commit("editTodo", { id, done: !todo.done });
|
commit("editTodo", { id, completed: !todo.completed });
|
||||||
},
|
},
|
||||||
clearCompleted({ state, commit }) {
|
clearCompleted({ state, commit }) {
|
||||||
state.todos
|
state.todos
|
||||||
.filter(todo => todo.done)
|
.filter(todo => todo.completed)
|
||||||
.forEach(todo => {
|
.forEach(todo => {
|
||||||
commit("removeTodo", todo.id);
|
commit("removeTodo", todo.id);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toggleAll({ state, commit }, done) {
|
toggleAll({ state, commit }, completed) {
|
||||||
state.todos.forEach(todo => {
|
state.todos.forEach(todo => {
|
||||||
commit("editTodo", { id: todo.id, done });
|
commit("editTodo", { id: todo.id, completed });
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
editTodo({ commit }, { id, title }) {
|
||||||
|
commit("editTodo", { id, title });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,35 +35,41 @@ const actions = {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
addTodo(state, text) {
|
addTodo(state, title) {
|
||||||
const id = state.nextId++;
|
const id = state.nextId++;
|
||||||
const todo = { id, text, done: false };
|
const todo = { id, title, completed: false };
|
||||||
state.todos.push(todo);
|
state.todos.push(todo);
|
||||||
},
|
},
|
||||||
removeTodo(state, id) {
|
removeTodo(state, id) {
|
||||||
const index = state.todos.findIndex(t => t.id === id);
|
const index = state.todos.findIndex(t => t.id === id);
|
||||||
state.todos.splice(index, 1);
|
state.todos.splice(index, 1);
|
||||||
},
|
},
|
||||||
editTodo(state, { id, text, done }) {
|
editTodo(state, { id, title, completed }) {
|
||||||
const todo = state.todos.find(t => t.id === id);
|
const todo = state.todos.find(t => t.id === id);
|
||||||
if (text !== undefined) {
|
if (title !== undefined) {
|
||||||
todo.text = text;
|
todo.title = title;
|
||||||
}
|
}
|
||||||
if (done !== undefined) {
|
if (completed !== undefined) {
|
||||||
todo.done = done;
|
todo.completed = completed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class TodoStore extends odoo.core.Store {
|
//------------------------------------------------------------------------------
|
||||||
commit(...args) {
|
// STORE
|
||||||
super.commit(...args);
|
//------------------------------------------------------------------------------
|
||||||
window.localStorage.setItem("todos", JSON.stringify(this.state.todos));
|
const LOCALSTORAGE_KEY = "todos-odoo";
|
||||||
}
|
|
||||||
}
|
|
||||||
export function makeStore() {
|
export function makeStore() {
|
||||||
const todos = JSON.parse(window.localStorage.getItem("todos") || "[]");
|
const todos = JSON.parse(
|
||||||
|
window.localStorage.getItem(LOCALSTORAGE_KEY) || "[]"
|
||||||
|
);
|
||||||
const nextId = Math.max(0, ...todos.map(t => t.id || 0)) + 1;
|
const nextId = Math.max(0, ...todos.map(t => t.id || 0)) + 1;
|
||||||
const state = { todos, nextId };
|
const state = { todos, nextId };
|
||||||
return new TodoStore({ state, actions, mutations });
|
const store = new odoo.core.Store({ state, actions, mutations });
|
||||||
|
store.on("update", null, () => {
|
||||||
|
const state = JSON.stringify(store.state.todos);
|
||||||
|
window.localStorage.setItem(LOCALSTORAGE_KEY, state);
|
||||||
|
});
|
||||||
|
return store;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<input class="toggle-all" id="toggle-all" type="checkbox" t-att-checked="allChecked" t-on-click="toggleAll"/>
|
<input class="toggle-all" id="toggle-all" type="checkbox" t-att-checked="allChecked" t-on-click="toggleAll"/>
|
||||||
<label for="toggle-all"></label>
|
<label for="toggle-all"></label>
|
||||||
<ul class="todo-list">
|
<ul class="todo-list">
|
||||||
<t t-foreach="todos" t-as="todo">
|
<t t-foreach="visibleTodos" t-as="todo">
|
||||||
<t t-widget="TodoItem" t-props="todo"/>
|
<t t-widget="TodoItem" t-props="todo"/>
|
||||||
</t>
|
</t>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -25,20 +25,32 @@
|
|||||||
</strong>
|
</strong>
|
||||||
<t t-esc="remainingText"/>
|
<t t-esc="remainingText"/>
|
||||||
</span>
|
</span>
|
||||||
|
<ul class="filters">
|
||||||
|
<li>
|
||||||
|
<a href="#/all" t-on-click="updateState({filter:'all'})" t-att-class="state.filter === 'all' ? 'selected' : ''">All</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#/active" t-on-click="updateState({filter:'active'})" t-att-class="state.filter === 'active' ? 'selected' : ''">Active</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#/completed" t-on-click="updateState({filter:'completed'})" t-att-class="state.filter === 'completed' ? 'selected' : ''">Completed</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<button class="clear-completed" t-if="todos.length gt remaining" t-on-click="clearCompleted">
|
<button class="clear-completed" t-if="todos.length gt remaining" t-on-click="clearCompleted">
|
||||||
Clear completed
|
Clear completed
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<li t-name="todoitem" class="todo">
|
<li t-name="todoitem" class="todo" t-att-class="(props.completed ? 'completed ' : ' ') + (state.isEditing ? 'editing' : '')">
|
||||||
<div class="view">
|
<div class="view">
|
||||||
<input class="toggle" type="checkbox" t-on-change="toggleTodo" t-att-checked="props.done"/>
|
<input class="toggle" type="checkbox" t-on-change="toggleTodo" t-att-checked="props.completed"/>
|
||||||
<label>
|
<label t-on-dblclick="editTodo">
|
||||||
<t t-esc="props.text"/>
|
<t t-esc="props.title"/>
|
||||||
</label>
|
</label>
|
||||||
<button class="destroy" t-on-click="removeTodo"></button>
|
<button class="destroy" t-on-click="removeTodo"></button>
|
||||||
</div>
|
</div>
|
||||||
|
<input class="edit" t-ref="input" t-if="state.isEditing" t-att-value="props.title" t-on-keyup="handleKeyup" t-on-blur="handleBlur"/>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ function updateAttrs(oldVnode: VNode, vnode: VNode): void {
|
|||||||
for (key in attrs) {
|
for (key in attrs) {
|
||||||
const cur = attrs[key];
|
const cur = attrs[key];
|
||||||
const old = oldAttrs[key];
|
const old = oldAttrs[key];
|
||||||
debugger;
|
|
||||||
if (old !== cur) {
|
if (old !== cur) {
|
||||||
if (cur === true) {
|
if (cur === true) {
|
||||||
elm.setAttribute(key, "");
|
elm.setAttribute(key, "");
|
||||||
|
|||||||
Reference in New Issue
Block a user