This is a simple playground to test Owl features.
-
-
-
+
+
+
+
+
Todo List
+
-
diff --git a/awesome_owl/static/src/todo/todo_item.js b/awesome_owl/static/src/todo/todo_item.js
new file mode 100644
index 00000000000..bdf6ac44990
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.js
@@ -0,0 +1,25 @@
+import { Component, useState } from "@odoo/owl";
+
+export class TodoItem extends Component {
+ static template = "awesome_owl.todo_item";
+
+ static props = {
+ todo: { type: Object, shape: {id: Number, description: String, isCompleted: Boolean} },
+ toggleState: { type: Function, optional: true },
+ removeTodo: { type: Function, optional: true },
+ };
+
+ setup() {}
+
+ toggle(event){
+ if(this.props.toggleState){
+ this.props.toggleState(this.props.todo.id, event.target.checked);
+ }
+ }
+
+ remove(){
+ if(this.props.removeTodo){
+ this.props.removeTodo(this.props.todo.id);
+ }
+ }
+}
diff --git a/awesome_owl/static/src/todo/todo_item.xml b/awesome_owl/static/src/todo/todo_item.xml
new file mode 100644
index 00000000000..75c343a6f2e
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_item.xml
@@ -0,0 +1,8 @@
+
+
+
+ ID: -
+
+
+
+
\ No newline at end of file
diff --git a/awesome_owl/static/src/todo/todo_list.js b/awesome_owl/static/src/todo/todo_list.js
new file mode 100644
index 00000000000..fa2121e06a2
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.js
@@ -0,0 +1,45 @@
+import { Component, useState, onMounted, } from "@odoo/owl";
+import { TodoItem } from "./todo_item";
+import { useAutofocus } from "../utils";
+
+
+export class TodoList extends Component {
+ static template = "awesome_owl.todo_list";
+ static components = { TodoItem };
+
+ static props = {};
+
+ setup() {
+ this.todos = useState([
+ { id: 1, description: "buy milk", isCompleted: true },
+ { id: 2, description: "buy cheese", isCompleted: false },
+ { id: 3, description: "buy bread", isCompleted: false }]);
+ useAutofocus(this, "todoInput");
+ }
+
+ addTodo(event) {
+ if(event.key === "Enter" && event.target.value.trim() !== ""){
+ const newId = this.todos.length ? Math.max(...this.todos.map(t => t.id)) +1 : 1;
+ this.todos.push({
+ id: newId,
+ description: event.target.value.trim(),
+ isCompleted: false
+ });
+ event.target.value = "";
+ }
+ }
+
+ removeTodo(elemId){
+ const index = this.todos.findIndex((elem) => elem.id === elemId);
+ if(index >= 0){
+ this.todos.splice(index, 1);
+ }
+ }
+
+ toggleTodoState(id, state){
+ const todo = this.todos.find(t => t.id === id);
+ if (todo) {
+ todo.isCompleted = state;
+ }
+ }
+}
diff --git a/awesome_owl/static/src/todo/todo_list.xml b/awesome_owl/static/src/todo/todo_list.xml
new file mode 100644
index 00000000000..78b1b4ea789
--- /dev/null
+++ b/awesome_owl/static/src/todo/todo_list.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/awesome_owl/static/src/utils.js b/awesome_owl/static/src/utils.js
new file mode 100644
index 00000000000..e9a152da5ac
--- /dev/null
+++ b/awesome_owl/static/src/utils.js
@@ -0,0 +1,21 @@
+/**
+ * useAutofocus - helper to focus an input element
+ * @param {Component} component - the component instance
+ * @param {string} refName - the t-ref name of the input element
+ */
+export function useAutofocus(component, refName) {
+ // Store original mounted method
+ const originalMounted = component.mounted;
+
+ component.mounted = function () {
+ // Call original mounted() if it exists
+ if (originalMounted) {
+ originalMounted.call(this);
+ }
+
+ // Focus the element
+ if (this.refs && this.refs[refName]) {
+ this.refs[refName].focus();
+ }
+ };
+}
diff --git a/tutorials b/tutorials
new file mode 160000
index 00000000000..696a0cc0644
--- /dev/null
+++ b/tutorials
@@ -0,0 +1 @@
+Subproject commit 696a0cc064431d4b62e2629fc34919d4a9a69ee5