Skip to content

Commit 73a7648

Browse files
author
seungwonme
committed
feat: tic-tac-toe
1 parent eca65bd commit 73a7648

File tree

15 files changed

+372
-46
lines changed

15 files changed

+372
-46
lines changed

frontend/react/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
# React
2+
3+
## Vite + React
4+
[Getting Started | Vite](https://vitejs.dev/guide/)

frontend/react/react-documents/src/components/Event.jsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ function MyBtn() {
44
const [count, setCount] = useState(0);
55
function handleClick() {
66
// alert("Click me?");
7-
setCount(count + 1)
7+
setCount(count + 1);
88
}
99

10-
return <button onClick={handleClick}>
11-
Clicked {count} times.
12-
</button>;
10+
return (
11+
<>
12+
<button onClick={handleClick}>Clicked {count} times.</button>;
13+
<button onClick={handleClick}>Clicked {count} times.</button>;
14+
</>
15+
);
1316
}
1417

1518
export default MyBtn;

frontend/react/react-documents/src/components/Hook.jsx

Whitespace-only changes.
+42-42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* http://meyerweb.com/eric/tools/css/reset/
2-
v2.0 | 20110126
3-
License: none (public domain)
2+
v2.0 | 20110126
3+
License: none (public domain)
44
*/
55
html, body, div, span, applet, object, iframe,
66
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
@@ -15,73 +15,73 @@ article, aside, canvas, details, embed,
1515
figure, figcaption, footer, header, hgroup,
1616
menu, nav, output, ruby, section, summary,
1717
time, mark, audio, video {
18-
margin: 0;
19-
padding: 0;
20-
border: 0;
21-
font-size: 100%;
22-
font: inherit;
23-
vertical-align: baseline;
18+
margin: 0;
19+
padding: 0;
20+
border: 0;
21+
font-size: 100%;
22+
font: inherit;
23+
vertical-align: baseline;
2424
}
2525
/* HTML5 display-role reset for older browsers */
2626
article, aside, details, figcaption, figure,
2727
footer, header, hgroup, menu, nav, section {
28-
display: block;
28+
display: block;
2929
}
3030
body {
31-
line-height: 1;
31+
line-height: 1;
3232
}
3333
ol, ul {
34-
list-style: none;
34+
list-style: none;
3535
}
3636
blockquote, q {
37-
quotes: none;
37+
quotes: none;
3838
}
3939
blockquote:before, blockquote:after,
4040
q:before, q:after {
41-
content: '';
42-
content: none;
41+
content: '';
42+
content: none;
4343
}
4444
table {
45-
border-collapse: collapse;
46-
border-spacing: 0;
45+
border-collapse: collapse;
46+
border-spacing: 0;
4747
}
4848
/* My custom reset */
4949
* {
50-
box-sizing: border-box;
50+
box-sizing: border-box;
5151
}
5252
a {
53-
text-decoration: none;
54-
color: inherit;
53+
text-decoration: none;
54+
color: inherit;
5555
}
5656
button {
57-
cursor: pointer;
58-
color: inherit;
59-
font-size: inherit;
60-
background-color: inherit;
61-
border:none;
62-
box-shadow:none;
63-
border-radius:0;
64-
padding:0;
65-
overflow:visible;
66-
cursor:pointer;
57+
cursor: pointer;
58+
color: inherit;
59+
font-size: inherit;
60+
background-color: inherit;
61+
border:none;
62+
box-shadow:none;
63+
border-radius:0;
64+
padding:0;
65+
overflow:visible;
66+
cursor:pointer;
6767
}
6868
input {
69-
font-size: inherit;
70-
background: inherit;
71-
border:none;
72-
box-shadow:none;
73-
border-radius:0;
74-
padding:0;
75-
overflow:visible;
76-
cursor:pointer;
69+
font-size: inherit;
70+
background: inherit;
71+
border:none;
72+
box-shadow:none;
73+
border-radius:0;
74+
padding:0;
75+
overflow:visible;
76+
cursor:pointer;
7777
}
7878
input:focus {
79-
outline: none;
79+
outline: none;
8080
}
8181
body {
82-
font-family: 'Roboto', sans-serif;
83-
background-color: #191B1C;
84-
color: #fff;
85-
overflow-x: hidden
82+
font-family: 'Roboto', sans-serif;
83+
background-color: #191B1C;
84+
color: #fff;
85+
overflow-x: hidden
8686
}
8787
/* End of custom reset */
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:react/recommended',
7+
'plugin:react/jsx-runtime',
8+
'plugin:react-hooks/recommended',
9+
],
10+
ignorePatterns: ['dist', '.eslintrc.cjs'],
11+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12+
settings: { react: { version: '18.2' } },
13+
plugins: ['react-refresh'],
14+
rules: {
15+
'react/jsx-no-target-blank': 'off',
16+
'react-refresh/only-export-components': [
17+
'warn',
18+
{ allowConstantExport: true },
19+
],
20+
},
21+
}

frontend/react/tic-tac-toe/.gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

frontend/react/tic-tac-toe/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# React + Vite
2+
3+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4+
5+
Currently, two official plugins are available:
6+
7+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

frontend/react/tic-tac-toe/bun.lockb

123 KB
Binary file not shown.

frontend/react/tic-tac-toe/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.jsx"></script>
12+
</body>
13+
</html>
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "tic-tac-toe",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "vite build",
9+
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"react": "^18.3.1",
14+
"react-dom": "^18.3.1"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^18.3.3",
18+
"@types/react-dom": "^18.3.0",
19+
"@vitejs/plugin-react": "^4.3.1",
20+
"eslint": "^8.57.0",
21+
"eslint-plugin-react": "^7.34.3",
22+
"eslint-plugin-react-hooks": "^4.6.2",
23+
"eslint-plugin-react-refresh": "^0.4.7",
24+
"vite": "^5.3.4"
25+
}
26+
}
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
body {
2+
background-color: #111;
3+
color: #fff;
4+
}
5+
6+
#root {
7+
display: flex;
8+
align-items: center;
9+
justify-content: center;
10+
height: 100vh;
11+
}
12+
13+
main {
14+
max-width: 300px;
15+
max-height: 300px;
16+
}
17+
18+
#status {
19+
position: absolute;
20+
top: 25%;
21+
left: 50%;
22+
transform: translate(-50%, -50%);
23+
}
24+
25+
.board-row {
26+
max-height: 100px;
27+
}
28+
29+
.square {
30+
float: left;
31+
justify-content: center;
32+
align-items: center;
33+
font-size: 50px;
34+
width: 100px;
35+
height: 100px;
36+
border: 1px solid white;
37+
}
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { useState } from "react";
2+
import "./App.css";
3+
4+
function Square({ value, onSquareClick }) {
5+
return (
6+
<button className="square" onClick={onSquareClick}>
7+
{value}
8+
</button>
9+
);
10+
}
11+
12+
function Board() {
13+
const [xIsNext, setXIsNext] = useState(true);
14+
const [squares, setSquares] = useState(Array(9).fill(null));
15+
16+
const winner = calculateWinner(squares);
17+
let status;
18+
if (winner) {
19+
status = "Winner: " + winner;
20+
} else {
21+
status = "Next player: " + (xIsNext ? "X" : "O");
22+
}
23+
24+
function handleClick(i) {
25+
if (squares[i] || calculateWinner(squares)) return;
26+
27+
const nextSquares = squares.slice();
28+
if (xIsNext) nextSquares[i] = "X";
29+
else nextSquares[i] = "O";
30+
setSquares(nextSquares);
31+
setXIsNext(!xIsNext);
32+
}
33+
34+
return (
35+
<>
36+
<div id="status">{status}</div>
37+
<div className="board-row">
38+
<Square value={squares[0]} onSquareClick={() => handleClick(0)} />
39+
<Square value={squares[1]} onSquareClick={() => handleClick(1)} />
40+
<Square value={squares[2]} onSquareClick={() => handleClick(2)} />
41+
</div>
42+
<div className="board-row">
43+
<Square value={squares[3]} onSquareClick={() => handleClick(3)} />
44+
<Square value={squares[4]} onSquareClick={() => handleClick(4)} />
45+
<Square value={squares[5]} onSquareClick={() => handleClick(5)} />
46+
</div>
47+
<div className="board-row">
48+
<Square value={squares[6]} onSquareClick={() => handleClick(6)} />
49+
<Square value={squares[7]} onSquareClick={() => handleClick(7)} />
50+
<Square value={squares[8]} onSquareClick={() => handleClick(8)} />
51+
</div>
52+
</>
53+
);
54+
}
55+
56+
function calculateWinner(squares) {
57+
const lines = [
58+
[0, 1, 2],
59+
[3, 4, 5],
60+
[6, 7, 8],
61+
[0, 3, 6],
62+
[1, 4, 7],
63+
[2, 5, 8],
64+
[0, 4, 8],
65+
[2, 4, 6],
66+
];
67+
68+
for (let i = 0; i < lines.length; i++) {
69+
const [a, b, c] = lines[i];
70+
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
71+
return squares[a];
72+
}
73+
}
74+
return null;
75+
}
76+
77+
function App() {
78+
return (
79+
<div id="root">
80+
<main>
81+
<Board />
82+
</main>
83+
</div>
84+
);
85+
}
86+
87+
export default App;
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import App from './App.jsx'
4+
import './reset.css'
5+
6+
ReactDOM.createRoot(document.getElementById('root')).render(
7+
<React.StrictMode>
8+
<App />
9+
</React.StrictMode>,
10+
)

0 commit comments

Comments
 (0)