Skip to content

Commit 29dc437

Browse files
committed
[IMP] playground: add a new sample
1 parent f8bb868 commit 29dc437

File tree

4 files changed

+185
-0
lines changed

4 files changed

+185
-0
lines changed

docs/playground/playground.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ const SAMPLES = [
9999
folder: "todo_app",
100100
code: ["js", "xml", "css"],
101101
},
102+
{
103+
description: "Tic-Tac-Toe (with reactivity)",
104+
folder: "tic_tac_toe",
105+
code: ["js", "xml", "css"],
106+
},
102107
{
103108
description: "Responsive app",
104109
folder: "responsive_app",
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.square {
2+
background: #fff;
3+
border: 1px solid #999;
4+
float: left;
5+
font-size: 24px;
6+
font-weight: bold;
7+
line-height: 34px;
8+
height: 34px;
9+
margin-right: -1px;
10+
margin-top: -1px;
11+
padding: 0;
12+
text-align: center;
13+
width: 34px;
14+
}
15+
16+
.board-row:after {
17+
clear: both;
18+
content: '';
19+
display: table;
20+
}
21+
22+
.status {
23+
margin-bottom: 10px;
24+
}
25+
.game {
26+
display: flex;
27+
flex-direction: row;
28+
}
29+
30+
.game-info {
31+
margin-left: 20px;
32+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// This example is an implementation of the Tic-Tac-Toe game, from
2+
// https://react.dev/learn/tutorial-tic-tac-toe. This is an easy application to start learning owl
3+
// with some interesting user interactions.
4+
//
5+
// In this implementation, we use the owl reactivity mechanism.
6+
import { Component, useState, mount } from "@odoo/owl";
7+
8+
9+
class Square extends Component {
10+
static template = "Square";
11+
}
12+
13+
class Board extends Component {
14+
static template = "Board"
15+
static components = { Square };
16+
17+
handleClick(i) {
18+
if (this.calculateWinner(this.props.squares) || this.props.squares[i]) {
19+
return;
20+
}
21+
const nextSquares = this.props.squares.slice();
22+
if (this.props.xIsNext) {
23+
nextSquares[i] = 'X';
24+
} else {
25+
nextSquares[i] = 'O';
26+
}
27+
this.props.onPlay(nextSquares);
28+
}
29+
30+
get status(){
31+
const winner = this.calculateWinner(this.props.squares);
32+
if (winner) {
33+
return 'Winner: ' + winner;
34+
} else {
35+
if (Object.values(this.props.squares).filter((v) => v === null).length > 0)
36+
return 'Next player: ' + (this.props.xIsNext ? 'X' : 'O');
37+
else
38+
return 'Draw';
39+
}
40+
}
41+
42+
calculateWinner(squares) {
43+
const lines = [
44+
[0, 1, 2],
45+
[3, 4, 5],
46+
[6, 7, 8],
47+
[0, 3, 6],
48+
[1, 4, 7],
49+
[2, 5, 8],
50+
[0, 4, 8],
51+
[2, 4, 6],
52+
];
53+
for (let i = 0; i < lines.length; i++) {
54+
const [a, b, c] = lines[i];
55+
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
56+
return squares[a];
57+
}
58+
}
59+
return null;
60+
}
61+
}
62+
63+
class Game extends Component {
64+
static template = "Game"
65+
static components = { Board };
66+
67+
setup() {
68+
this.state = useState({
69+
currentMove: 0,
70+
history: [Array(9).fill(null)],
71+
});
72+
}
73+
74+
get currentSquares() {
75+
return this.state.history[this.state.currentMove];
76+
}
77+
78+
get xIsNext() {
79+
return this.state.currentMove % 2 === 0;
80+
}
81+
82+
jumpTo(nextMove) {
83+
this.state.currentMove = nextMove;
84+
}
85+
86+
handlePlay(nextSquares) {
87+
const nextHistory = [...this.state.history.slice(0, this.state.currentMove + 1), nextSquares];
88+
this.state.history = nextHistory;
89+
this.state.currentMove = this.state.history.length - 1;
90+
}
91+
92+
get moves() {
93+
return this.state.history.map((_squares, move) => {
94+
if (move > 0) {
95+
return {id: move, description: 'Go to move #' + move};
96+
} else {
97+
return {id: move, description: 'Go to game start'};
98+
}
99+
});
100+
}
101+
102+
}
103+
104+
// Application setup
105+
mount(Game, document.body, { templates: TEMPLATES, dev: true});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<templates>
2+
<button t-name="Square" class="square" t-on-click="props.onSquareClick">
3+
<t t-esc="props.value"/>
4+
</button>
5+
6+
<t t-name="Board">
7+
<div class="status">
8+
<t t-esc="status"/>
9+
</div>
10+
<div class="board-row">
11+
<Square value="props.squares[0]" onSquareClick="() => this.handleClick(0)" />
12+
<Square value="props.squares[1]" onSquareClick="() => this.handleClick(1)" />
13+
<Square value="props.squares[2]" onSquareClick="() => this.handleClick(2)" />
14+
</div>
15+
<div class="board-row">
16+
<Square value="props.squares[3]" onSquareClick="() => this.handleClick(3)" />
17+
<Square value="props.squares[4]" onSquareClick="() => this.handleClick(4)" />
18+
<Square value="props.squares[5]" onSquareClick="() => this.handleClick(5)" />
19+
</div>
20+
<div class="board-row">
21+
<Square value="props.squares[6]" onSquareClick="() => this.handleClick(6)" />
22+
<Square value="props.squares[7]" onSquareClick="() => this.handleClick(7)" />
23+
<Square value="props.squares[8]" onSquareClick="() => this.handleClick(8)" />
24+
</div>
25+
</t>
26+
27+
<div t-name="Game" class="game">
28+
<div class="game-board">
29+
<Board xIsNext="xIsNext" squares="currentSquares" onPlay.bind="handlePlay" />
30+
</div>
31+
<div class="game-info">
32+
<ol>
33+
<t t-foreach="moves" t-as="move" t-key="move.id">
34+
<li>
35+
<button t-on-click="() => this.jumpTo(move.id)">
36+
<t t-esc="move.description"/>
37+
</button>
38+
</li>
39+
</t>
40+
</ol>
41+
</div>
42+
</div>
43+
</templates>

0 commit comments

Comments
 (0)