From 91a463c5245a03a4ef31110b49d8c0ca60fb2780 Mon Sep 17 00:00:00 2001 From: sreedevk Date: Wed, 27 May 2026 09:41:56 +0000 Subject: [PATCH] [ADD] awesome owl: Framework Tutorials 101 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PURPOSE Learn the basics of the Owl framework and of the Odoo web framework, which is built on top. task-6250237 --- awesome_owl/static/src/card/card.js | 9 +++ awesome_owl/static/src/card/card.xml | 12 ++++ awesome_owl/static/src/counter/counter.js | 14 ++++ awesome_owl/static/src/counter/counter.xml | 15 ++++ awesome_owl/static/src/main.js | 4 +- awesome_owl/static/src/playground.js | 16 ++++- awesome_owl/static/src/playground.xml | 18 +++-- awesome_owl/static/src/todo/todo_item.js | 11 +++ awesome_owl/static/src/todo/todo_item.xml | 31 ++++++++ awesome_owl/static/src/todo/todo_list.js | 49 +++++++++++++ awesome_owl/static/src/todo/todo_list.xml | 26 +++++++ ruff.toml | 84 ++++++++++++++++++++++ 12 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 awesome_owl/static/src/card/card.js create mode 100644 awesome_owl/static/src/card/card.xml create mode 100644 awesome_owl/static/src/counter/counter.js create mode 100644 awesome_owl/static/src/counter/counter.xml create mode 100644 awesome_owl/static/src/todo/todo_item.js create mode 100644 awesome_owl/static/src/todo/todo_item.xml create mode 100644 awesome_owl/static/src/todo/todo_list.js create mode 100644 awesome_owl/static/src/todo/todo_list.xml create mode 100644 ruff.toml diff --git a/awesome_owl/static/src/card/card.js b/awesome_owl/static/src/card/card.js new file mode 100644 index 00000000000..f934fbf140f --- /dev/null +++ b/awesome_owl/static/src/card/card.js @@ -0,0 +1,9 @@ +import { Component } from "@odoo/owl"; + +export class Card extends Component { + static template = "awesome_owl.card"; + static props = { + title: String, + content: String, + }; +} diff --git a/awesome_owl/static/src/card/card.xml b/awesome_owl/static/src/card/card.xml new file mode 100644 index 00000000000..6642de5162b --- /dev/null +++ b/awesome_owl/static/src/card/card.xml @@ -0,0 +1,12 @@ + + + +
+
+
+

+

+
+
+
+ diff --git a/awesome_owl/static/src/counter/counter.js b/awesome_owl/static/src/counter/counter.js new file mode 100644 index 00000000000..fbbdc8c4b49 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.js @@ -0,0 +1,14 @@ +import { Component, useState } from '@odoo/owl' + +export class Counter extends Component { + static template = "awesome_owl.counter"; + + setup() { + this.state = useState({ value: 0 }); + } + + increment() { + this.state.value++; + this.props.onChange(); + } +} diff --git a/awesome_owl/static/src/counter/counter.xml b/awesome_owl/static/src/counter/counter.xml new file mode 100644 index 00000000000..70ff85bd928 --- /dev/null +++ b/awesome_owl/static/src/counter/counter.xml @@ -0,0 +1,15 @@ + + + +
+

Owl Counter

+

+ Counter: +

+ +
+
+
+ diff --git a/awesome_owl/static/src/main.js b/awesome_owl/static/src/main.js index 1aaea902b55..676a2d1a86a 100644 --- a/awesome_owl/static/src/main.js +++ b/awesome_owl/static/src/main.js @@ -3,8 +3,8 @@ import { mountComponent } from "@web/env"; import { Playground } from "./playground"; const config = { - dev: true, - name: "Owl Tutorial" + dev: true, + name: "Owl Tutorial" }; // Mount the Playground component when the document.body is ready diff --git a/awesome_owl/static/src/playground.js b/awesome_owl/static/src/playground.js index 4ac769b0aa5..d22585ea73a 100644 --- a/awesome_owl/static/src/playground.js +++ b/awesome_owl/static/src/playground.js @@ -1,5 +1,17 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; +import { Counter } from "./counter/counter" +import { Card } from "./card/card" +import { TodoList } from "./todo/todo_list"; export class Playground extends Component { - static template = "awesome_owl.playground"; + static template = "awesome_owl.playground" + static components = { Counter, Card, TodoList }; + + setup() { + this.state = useState({ sum: 0 }); + } + + updateSum() { + this.state.sum++; + } } diff --git a/awesome_owl/static/src/playground.xml b/awesome_owl/static/src/playground.xml index 4fb905d59f9..9bf15e3141f 100644 --- a/awesome_owl/static/src/playground.xml +++ b/awesome_owl/static/src/playground.xml @@ -1,10 +1,20 @@ - -
- hello world + + +
+ + + +
+

Todo

+
+ +
- + 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..78b9e1c8d22 --- /dev/null +++ b/awesome_owl/static/src/todo/todo_item.js @@ -0,0 +1,11 @@ +import { Component } from "@odoo/owl"; + +export class TodoItem extends Component { + static template = "awesome_owl.todo_item"; + + static props = { + todo: Object, + toggleState: Function, + removeTodo: Function, + }; +} 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..9e2fe58fef6 --- /dev/null +++ b/awesome_owl/static/src/todo/todo_item.xml @@ -0,0 +1,31 @@ + + + +
+ +
+ + + # + +
+ +
+ + Done + + + Pending + + + +
+
+
+ +
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..0a90c4e2032 --- /dev/null +++ b/awesome_owl/static/src/todo/todo_list.js @@ -0,0 +1,49 @@ +import { Component, useState, useRef, onMounted } from "@odoo/owl"; +import { TodoItem } from "../todo/todo_item"; + +export class TodoList extends Component { + static template = "awesome_owl.todo_list"; + static components = { TodoItem }; + + setup() { + this.items = useState([{ id: 1, description: "Buy milk", isCompleted: true }]); + this.nextId = 2; + this.inputRef = useRef("input"); + + onMounted(() => { + if (this.inputRef.el) this.inputRef.el.focus(); + }); + } + + createItem(ev) { + if (ev.keyCode === 13) { + const description = ev.target.value.trim(); + + if (!description) { + return; + } + + this.items.push({ + id: this.nextId++, + description: description, + isCompleted: false + }); + + ev.target.value = ""; + } + } + + toggleItem(item_id) { + const todo = this.items.find(t => t.id === item_id); + if (todo) { + todo.isCompleted = !todo.isCompleted; + } + } + + deleteItem(item_id) { + const index = this.items.findIndex((t) => t.id === item_id); + if (index >= 0) { + this.items.splice(index, 1); + } + } +} 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..f9fe77f225c --- /dev/null +++ b/awesome_owl/static/src/todo/todo_list.xml @@ -0,0 +1,26 @@ + + + +
+ +
+ +
+ + + + + + + +

You currently have on tasks.

+
+
+
+
diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000000..26df8ffc370 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,84 @@ +# automatically generated file by the runbot nightly ruff checks, do not modify +# for ruff version 0.15.0 (or higher) +# note: 'E241', 'E272', 'E201', 'E221' are ignored on runbot in test files when formating a table like structure (more than two space) +# some rules present here are not enabled on runbot (yet) but are still advised to follow when possible : ["B904", "COM812", "E741", "EM101", "I001", "RET", "RUF021", "RUF102", "TRY002", "UP006", "UP007"] + + +target-version = "py310" + +[lint] +preview = true +external = ["OLS"] +select = [ + "BLE", # flake8-blind-except + "C", # flake8-comprehensions + "COM", # flake8-commas + "E", # pycodestyle Error + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FLY", # flynt + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PLC", # Pylint Convention + "PLE", # Pylint Error + "PLW", # Pylint Warning + "PYI", # flake8-pyi + "RET", # flake8-return + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "SLOT", # flake8-slots + "T", # flake8-print + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports + "TRY", # tryceratops + "UP", # pyupgrade + "W", # pycodestyle Warning + "YTT", # flake8-2020 +] +ignore = [ + "C408", # unnecessary-collection-call + "C420", # unnecessary-dict-comprehension-for-iterable + "C901", # complex-structure + "E266", # multiple-leading-hashes-for-block-comment + "E501", # line-too-long + "E713", # not-in-test + "EM102", # f-string-in-exception + "FA100", # future-rewritable-type-annotation + "PGH003", # blanket-type-ignore + "PIE790", # unnecessary-placeholder + "PIE808", # unnecessary-range-start + "PLC2701", # import-private-name + "PLW2901", # redefined-loop-name + "RUF001", # ambiguous-unicode-character-string + "RUF005", # collection-literal-concatenation + "RUF012", # mutable-class-default + "RUF067", # non-empty-init-module + "RUF100", # unused-noqa + "SIM102", # collapsible-if + "SIM108", # if-else-block-instead-of-if-exp + "SIM117", # multiple-with-statements + "TID252", # relative-imports + "TRY003", # raise-vanilla-args + "TRY300", # try-consider-else + "TRY400", # error-instead-of-exception + "UP031", # printf-string-formatting +] + +[lint.per-file-ignores] +"**/__init__.py" = [ + "F401", # unused-import +] + +[lint.isort] +# https://www.odoo.com/documentation/latest/contributing/development/coding_guidelines.html#imports +section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] +known-first-party = ["odoo"] +known-local-folder = ["odoo.addons"]