mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] playground: add a new sample
This commit is contained in:
@@ -99,6 +99,11 @@ const SAMPLES = [
|
||||
folder: "todo_app",
|
||||
code: ["js", "xml", "css"],
|
||||
},
|
||||
{
|
||||
description: "Tic-Tac-Toe (with reactivity)",
|
||||
folder: "tic_tac_toe",
|
||||
code: ["js", "xml", "css"],
|
||||
},
|
||||
{
|
||||
description: "Responsive app",
|
||||
folder: "responsive_app",
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
.square {
|
||||
background: #fff;
|
||||
border: 1px solid #999;
|
||||
float: left;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 34px;
|
||||
height: 34px;
|
||||
margin-right: -1px;
|
||||
margin-top: -1px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 34px;
|
||||
}
|
||||
|
||||
.board-row:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.game {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.game-info {
|
||||
margin-left: 20px;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// This example is an implementation of the Tic-Tac-Toe game, from
|
||||
// https://react.dev/learn/tutorial-tic-tac-toe. This is an easy application to start learning owl
|
||||
// with some interesting user interactions.
|
||||
//
|
||||
// In this implementation, we use the owl reactivity mechanism.
|
||||
import { Component, useState, mount } from "@odoo/owl";
|
||||
|
||||
|
||||
class Square extends Component {
|
||||
static template = "Square";
|
||||
}
|
||||
|
||||
class Board extends Component {
|
||||
static template = "Board"
|
||||
static components = { Square };
|
||||
|
||||
handleClick(i) {
|
||||
if (this.calculateWinner(this.props.squares) || this.props.squares[i]) {
|
||||
return;
|
||||
}
|
||||
const nextSquares = this.props.squares.slice();
|
||||
if (this.props.xIsNext) {
|
||||
nextSquares[i] = 'X';
|
||||
} else {
|
||||
nextSquares[i] = 'O';
|
||||
}
|
||||
this.props.onPlay(nextSquares);
|
||||
}
|
||||
|
||||
get status(){
|
||||
const winner = this.calculateWinner(this.props.squares);
|
||||
if (winner) {
|
||||
return 'Winner: ' + winner;
|
||||
} else {
|
||||
if (Object.values(this.props.squares).filter((v) => v === null).length > 0)
|
||||
return 'Next player: ' + (this.props.xIsNext ? 'X' : 'O');
|
||||
else
|
||||
return 'Draw';
|
||||
}
|
||||
}
|
||||
|
||||
calculateWinner(squares) {
|
||||
const lines = [
|
||||
[0, 1, 2],
|
||||
[3, 4, 5],
|
||||
[6, 7, 8],
|
||||
[0, 3, 6],
|
||||
[1, 4, 7],
|
||||
[2, 5, 8],
|
||||
[0, 4, 8],
|
||||
[2, 4, 6],
|
||||
];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const [a, b, c] = lines[i];
|
||||
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
|
||||
return squares[a];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Game extends Component {
|
||||
static template = "Game"
|
||||
static components = { Board };
|
||||
|
||||
setup() {
|
||||
this.state = useState({
|
||||
currentMove: 0,
|
||||
history: [Array(9).fill(null)],
|
||||
});
|
||||
}
|
||||
|
||||
get currentSquares() {
|
||||
return this.state.history[this.state.currentMove];
|
||||
}
|
||||
|
||||
get xIsNext() {
|
||||
return this.state.currentMove % 2 === 0;
|
||||
}
|
||||
|
||||
jumpTo(nextMove) {
|
||||
this.state.currentMove = nextMove;
|
||||
}
|
||||
|
||||
handlePlay(nextSquares) {
|
||||
const nextHistory = [...this.state.history.slice(0, this.state.currentMove + 1), nextSquares];
|
||||
this.state.history = nextHistory;
|
||||
this.state.currentMove = this.state.history.length - 1;
|
||||
}
|
||||
|
||||
get moves() {
|
||||
return this.state.history.map((_squares, move) => {
|
||||
if (move > 0) {
|
||||
return {id: move, description: 'Go to move #' + move};
|
||||
} else {
|
||||
return {id: move, description: 'Go to game start'};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Application setup
|
||||
mount(Game, document.body, { templates: TEMPLATES, dev: true});
|
||||
@@ -0,0 +1,43 @@
|
||||
<templates>
|
||||
<button t-name="Square" class="square" t-on-click="props.onSquareClick">
|
||||
<t t-esc="props.value"/>
|
||||
</button>
|
||||
|
||||
<t t-name="Board">
|
||||
<div class="status">
|
||||
<t t-esc="status"/>
|
||||
</div>
|
||||
<div class="board-row">
|
||||
<Square value="props.squares[0]" onSquareClick="() => this.handleClick(0)" />
|
||||
<Square value="props.squares[1]" onSquareClick="() => this.handleClick(1)" />
|
||||
<Square value="props.squares[2]" onSquareClick="() => this.handleClick(2)" />
|
||||
</div>
|
||||
<div class="board-row">
|
||||
<Square value="props.squares[3]" onSquareClick="() => this.handleClick(3)" />
|
||||
<Square value="props.squares[4]" onSquareClick="() => this.handleClick(4)" />
|
||||
<Square value="props.squares[5]" onSquareClick="() => this.handleClick(5)" />
|
||||
</div>
|
||||
<div class="board-row">
|
||||
<Square value="props.squares[6]" onSquareClick="() => this.handleClick(6)" />
|
||||
<Square value="props.squares[7]" onSquareClick="() => this.handleClick(7)" />
|
||||
<Square value="props.squares[8]" onSquareClick="() => this.handleClick(8)" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-name="Game" class="game">
|
||||
<div class="game-board">
|
||||
<Board xIsNext="xIsNext" squares="currentSquares" onPlay.bind="handlePlay" />
|
||||
</div>
|
||||
<div class="game-info">
|
||||
<ol>
|
||||
<t t-foreach="moves" t-as="move" t-key="move.id">
|
||||
<li>
|
||||
<button t-on-click="() => this.jumpTo(move.id)">
|
||||
<t t-esc="move.description"/>
|
||||
</button>
|
||||
</li>
|
||||
</t>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</templates>
|
||||
Reference in New Issue
Block a user