Skip to content

Commit aa0188c

Browse files
committed
[IMP] awesome_owl: Build interactive UI with reusable components in OWL
Purpose: Build reusable components and interactive UI features using the OWL framework. Approach: Created counter and card components with props and slots. Added todo list with add, delete, toggle, and focus features. Used markup, dynamic attributes, and basic lifecycle hooks. Impact: Improves understanding of OWL concepts and sets up a base for building interactive UIs.
1 parent 4bfe378 commit aa0188c

19 files changed

+349
-70
lines changed

awesome_owl/__manifest__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
('include', 'web._assets_core'),
3737
'web/static/src/libs/fontawesome/css/font-awesome.css',
3838
'awesome_owl/static/src/**/*',
39+
# "awesome_owl/static/src/components/playground.js",
40+
# "awesome_owl/static/src/components/playground.xml",
41+
# "awesome_owl/static/src/counter/counter.js",
42+
# "awesome_owl/static/src/counter/counter.xml",
3943
],
4044
},
4145
'license': 'AGPL-3'

awesome_owl/static/src/card/card.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
5+
export class Card extends Component {
6+
static template = "awesome_owl.Card";
7+
8+
static props = {
9+
title: { type: String, optional: true },
10+
content: { type: String, optional: true },
11+
heading: { type: String, optional: true },
12+
describtion: { type: String, optional: true },
13+
slots: {type: Object, optional: true, shape: { default: true }},
14+
};
15+
setup() {
16+
this.state = useState({ isOpen: true });
17+
}
18+
19+
toggleContent() {
20+
this.state.isOpen = !this.state.isOpen;
21+
}
22+
}

awesome_owl/static/src/card/card.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_owl.Card">
4+
<div class="card d-inline-block m-2" style="width: 18rem;">
5+
<div class="card-body">
6+
<h5 class="card-title">
7+
<t t-out="props.title"/>
8+
<button class="btn" t-on-click="toggleContent">Toggle</button>
9+
</h5>
10+
<p class="card-text" t-if="state.isOpen">
11+
<p class="card-text"><t t-esc="props.content"/></p>
12+
<p class="card-text"><t t-slot="default"/></p>
13+
<h5 class="card-title"><t t-out="props.heading"/></h5>
14+
<p class="card-text"><t t-out="props.describtion"/></p>
15+
</p>
16+
</div>
17+
</div>
18+
</t>
19+
</templates>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
5+
export class Counter extends Component {
6+
static template = "awesome_owl.counter";
7+
static props = {
8+
onChange: { type: Function, optional: true }
9+
};
10+
setup() {
11+
this.state = useState({ value: 1 });
12+
}
13+
increment() {
14+
this.state.value = this.state.value + 1;
15+
if (this.props.onChange) {
16+
this.props.onChange();
17+
}
18+
}
19+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_owl.counter">
4+
<div
5+
style="border: 1px solid #ccc;
6+
padding: 10px;
7+
display: inline-flex;
8+
align-items: center;
9+
gap: 10px;
10+
border-radius: 4px;"
11+
>
12+
<p style="margin: 0;">Counter: <t t-esc="state.value"/></p>
13+
<button t-on-click="increment"
14+
style="background-color: #8660c4;
15+
color: white;
16+
border: none;
17+
padding: 6px 12px;
18+
border-radius: 4px;">
19+
Increment
20+
</button>
21+
</div>
22+
</t>
23+
</templates>

awesome_owl/static/src/playground.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
/** @odoo-module **/
22

3-
import { Component } from "@odoo/owl";
3+
import { Component, markup, useState } from "@odoo/owl";
4+
import { Counter } from "./counter/counter";
5+
import { Card } from "./card/card";
6+
import { TodoList } from "./todo_list/todo_list";
47

58
export class Playground extends Component {
69
static template = "awesome_owl.playground";
10+
static props = {
11+
onChange: { type: Function, optional: true }
12+
};
13+
static components = { Counter, Card, TodoList };
14+
15+
setup() {
16+
this.str1 = "<div class='text-primary'>some content</div>";
17+
this.str2 = markup("<div class='text-primary'>some content</div>");
18+
this.sum = useState({ value: 2 });
19+
}
20+
21+
incrementSum() {
22+
this.sum.value++;
23+
}
724
}

awesome_owl/static/src/playground.xml

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,83 @@
1-
<?xml version="1.0" encoding="UTF-8" ?>
1+
<?xml version="1.0" encoding="UTF-8"?>
22
<templates xml:space="preserve">
3-
4-
<t t-name="awesome_owl.playground">
5-
<div class="p-3">
6-
hello world
7-
</div>
3+
<t t-name="awesome_owl.playground">
4+
<h2 class="text-center mb-2 mt-4">Counters</h2>
5+
<div style="
6+
display: inline-flex;
7+
border: 1px solid #ccc;
8+
border-radius: 8px;
9+
padding: 16px;
10+
gap: 16px;
11+
align-items: center;
12+
margin-left: 40px;
13+
">
14+
<span style="font-size: 18px;">hello world</span>
15+
<Counter />
16+
<Counter />
17+
</div>
18+
<h2 class="text-center mb-2 mt-4">Cards</h2>
19+
<div style="
20+
display: inline-flex;
21+
border: 1px solid #ccc;
22+
border-radius: 8px;
23+
padding: 16px;
24+
gap: 16px;
25+
align-items: center;
26+
margin-left: 40px;
27+
">
28+
<Card title="'card 1'" content="'content of card 1'"/>
29+
<Card title="'card 2'" content="'content of card 2'"/>
30+
</div>
31+
<h2 class="text-center mb-2 mt-4">Cards with Markup</h2>
32+
<div style="
33+
display: inline-flex;
34+
border: 1px solid #ccc;
35+
border-radius: 8px;
36+
padding: 16px;
37+
gap: 16px;
38+
align-items: center;
39+
margin-left: 40px;
40+
">
41+
<Card heading="'card 1'" describtion="str1"/>
42+
<Card heading="'card 2'" describtion="str2"/>
43+
</div>
44+
<h2 class="text-center mb-2 mt-4">Sum of counters</h2>
45+
<div style="
46+
display: inline-flex;
47+
border: 1px solid #ccc;
48+
border-radius: 8px;
49+
padding: 16px;
50+
gap: 16px;
51+
align-items: center;
52+
margin-left: 40px;
53+
">
54+
<div>The sum is: <t t-esc="sum.value"/></div>
55+
<Counter onChange.bind="incrementSum" />
56+
<Counter onChange.bind="incrementSum" />
57+
</div>
58+
<h2 class="text-center mb-2 mt-4">Todo List</h2>
59+
<div style="
60+
display: inline-flex;
61+
border: 1px solid #ccc;
62+
border-radius: 8px;
63+
padding: 16px;
64+
gap: 16px;
65+
align-items: center;
66+
margin-left: 40px;
67+
">
68+
<TodoList />
69+
</div>
70+
<h2 class="text-center mb-2 mt-4">Generic card with slots and toggle</h2>
71+
<div style="
72+
display: inline-flex;
73+
border: 1px solid #ccc;
74+
border-radius: 8px;
75+
padding: 16px;
76+
gap: 16px;
77+
align-items: center;
78+
margin-left: 40px;
79+
">
80+
<Card title="'card 2'"><Counter /></Card>
81+
</div>
882
</t>
9-
1083
</templates>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** @odoo-module **/
2+
3+
import { Component } from "@odoo/owl";
4+
5+
export class TodoItem extends Component {
6+
static template = "awesome_owl.TodoItem";
7+
static props = {
8+
todo: {
9+
type: Object,
10+
shape: { id: Number, description: String, isCompleted: Boolean }
11+
},
12+
toggleState: Function,
13+
removeTodo: Function,
14+
};
15+
onChange() {
16+
this.props.toggleState(this.props.todo.id);
17+
}
18+
onRemove() {
19+
this.props.removeTodo(this.props.todo.id);
20+
}
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<t t-name="awesome_owl.TodoItem">
4+
<div class="form-check">
5+
<input
6+
class="form-check-input"
7+
type="checkbox"
8+
t-att-id="props.todo.id"
9+
t-att-checked="props.todo.isCompleted"
10+
t-on-change="onChange"/>
11+
<label
12+
t-att-for="props.todo.id"
13+
t-att-class="props.todo.isCompleted ? 'text-decoration-line-through text-muted' : '' ">
14+
<t t-esc="props.todo.id"/>
15+
<t t-esc="props.todo.description"/>
16+
</label>
17+
<span role="button" class="fa fa-trash ms-3 text-danger" t-on-click="onRemove"/>
18+
</div>
19+
</t>
20+
</templates>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/** @odoo-module **/
2+
3+
import { Component, useState } from "@odoo/owl";
4+
import { TodoItem } from "./todo_item";
5+
import { useAutofocus } from "../utils";
6+
7+
export class TodoList extends Component {
8+
static template = "awesome_owl.TodoList";
9+
static components = { TodoItem };
10+
11+
setup() {
12+
this.nextId = 0;
13+
this.newtodo = useState([]);
14+
useAutofocus("input")
15+
}
16+
addTodo(ev) {
17+
if (ev.keyCode === 13 && ev.target.value != "") {
18+
this.newtodo.push({
19+
id: this.nextId++,
20+
description: ev.target.value,
21+
isCompleted: false
22+
});
23+
ev.target.value = "";
24+
}
25+
}
26+
toggleTodo(todoId) {
27+
const todo = this.newtodo.find((todo) => todo.id === todoId);
28+
if (todo) {
29+
todo.isCompleted = !todo.isCompleted;
30+
}
31+
}
32+
removeTodo(todoId) {
33+
const todoIndex = this.newtodo.findIndex((todo) => todo.id === todoId);
34+
if (todoIndex >= 0) {
35+
this.newtodo.splice(todoIndex, 1);
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)