dao-game / commit
Initial port of local game to React.
.gitignore | 2 +-
.vscode/launch.json | 10 +-
package-lock.json | 670 +++++++++++++++------
package.json | 16 +-
public/index.html | 2 +-
public/local.html | 2 +-
public/react.html | 17 +
public/remote.html | 2 +-
readme.md | 2 +
src/app.tsx | 11 +
src/game/coord.ts | 8 +-
src/game/game-board.ts | 49 +-
src/game/game.ts | 20 +-
src/game/local-game.ts | 8 +-
src/game/remote-game.ts | 22 +-
src/react/App.css | 25 +
src/react/App.tsx | 111 ++++
src/react/Base.css | 29 +
src/react/Controls.css | 31 +
src/react/Controls.tsx | 27 +
src/react/Piece.css | 51 ++
src/react/Piece.tsx | 22 +
src/react/Status.css | 34 ++
src/react/Status.tsx | 22 +
src/react/Tile.css | 32 +
src/react/Tile.tsx | 46 ++
...build-build-and-serve.ts => build-and-serve.ts} | 2 +-
tools/build.ts | 6 +
tools/esbuild-watch.ts | 6 -
tools/esbuild.ts | 6 -
tools/{esbuild-settings.ts => settings.ts} | 14 +-
tsconfig.json | 93 +++
32 files changed, 1141 insertions(+), 257 deletions(-)
diff --git a/.gitignore b/.gitignore
index 6760a00..f511f81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,4 @@
/.git/*
# artifacts
-/public/js
+/public/dist
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 98ef16e..1b8c55b 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -27,6 +27,14 @@
"url": "http://localhost:3000/remote.html",
"webRoot": "${workspaceFolder}/src",
// "preLaunchTask": "task: npm: start"
- }
+ },
+ {
+ "type": "pwa-chrome",
+ "request": "launch",
+ "name": "Debug React Landing Page",
+ "url": "http://localhost:3000/react.html",
+ "webRoot": "${workspaceFolder}/src",
+ // "preLaunchTask": "task: npm: start"
+ },
]
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6db6ab6..e0567e3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,12 +10,17 @@
"license": "ISC",
"dependencies": {
"detect-browser": "^5.3.0",
- "esbuild": "^0.14.25",
"peerjs": "^1.3.2",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-modal": "^3.14.4",
"web-animations-js": "^2.3.2"
},
"devDependencies": {
- "@types/node": "^17.0.21",
+ "@types/node": "^17.0.23",
+ "@types/react": "^17.0.42",
+ "@types/react-modal": "^3.13.1",
+ "esbuild": "^0.14.27",
"rimraf": "^3.0.2",
"ts-node": "^10.6.0",
"typescript": "^4.6.2"
@@ -67,9 +72,41 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "17.0.21",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
- "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
+ "version": "17.0.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+ "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+ "dev": true
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.4",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+ "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==",
+ "dev": true
+ },
+ "node_modules/@types/react": {
+ "version": "17.0.42",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz",
+ "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-modal": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
+ "integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"node_modules/acorn": {
@@ -127,6 +164,12 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
+ "node_modules/csstype": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
+ "dev": true
+ },
"node_modules/detect-browser": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz",
@@ -142,9 +185,10 @@
}
},
"node_modules/esbuild": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.25.tgz",
- "integrity": "sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.27.tgz",
+ "integrity": "sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==",
+ "dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -153,35 +197,36 @@
"node": ">=12"
},
"optionalDependencies": {
- "esbuild-android-64": "0.14.25",
- "esbuild-android-arm64": "0.14.25",
- "esbuild-darwin-64": "0.14.25",
- "esbuild-darwin-arm64": "0.14.25",
- "esbuild-freebsd-64": "0.14.25",
- "esbuild-freebsd-arm64": "0.14.25",
- "esbuild-linux-32": "0.14.25",
- "esbuild-linux-64": "0.14.25",
- "esbuild-linux-arm": "0.14.25",
- "esbuild-linux-arm64": "0.14.25",
- "esbuild-linux-mips64le": "0.14.25",
- "esbuild-linux-ppc64le": "0.14.25",
- "esbuild-linux-riscv64": "0.14.25",
- "esbuild-linux-s390x": "0.14.25",
- "esbuild-netbsd-64": "0.14.25",
- "esbuild-openbsd-64": "0.14.25",
- "esbuild-sunos-64": "0.14.25",
- "esbuild-windows-32": "0.14.25",
- "esbuild-windows-64": "0.14.25",
- "esbuild-windows-arm64": "0.14.25"
+ "esbuild-android-64": "0.14.27",
+ "esbuild-android-arm64": "0.14.27",
+ "esbuild-darwin-64": "0.14.27",
+ "esbuild-darwin-arm64": "0.14.27",
+ "esbuild-freebsd-64": "0.14.27",
+ "esbuild-freebsd-arm64": "0.14.27",
+ "esbuild-linux-32": "0.14.27",
+ "esbuild-linux-64": "0.14.27",
+ "esbuild-linux-arm": "0.14.27",
+ "esbuild-linux-arm64": "0.14.27",
+ "esbuild-linux-mips64le": "0.14.27",
+ "esbuild-linux-ppc64le": "0.14.27",
+ "esbuild-linux-riscv64": "0.14.27",
+ "esbuild-linux-s390x": "0.14.27",
+ "esbuild-netbsd-64": "0.14.27",
+ "esbuild-openbsd-64": "0.14.27",
+ "esbuild-sunos-64": "0.14.27",
+ "esbuild-windows-32": "0.14.27",
+ "esbuild-windows-64": "0.14.27",
+ "esbuild-windows-arm64": "0.14.27"
}
},
"node_modules/esbuild-android-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz",
- "integrity": "sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz",
+ "integrity": "sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"android"
@@ -191,12 +236,13 @@
}
},
"node_modules/esbuild-android-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz",
- "integrity": "sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz",
+ "integrity": "sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==",
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"android"
@@ -206,12 +252,13 @@
}
},
"node_modules/esbuild-darwin-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz",
- "integrity": "sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz",
+ "integrity": "sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"darwin"
@@ -221,12 +268,13 @@
}
},
"node_modules/esbuild-darwin-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz",
- "integrity": "sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz",
+ "integrity": "sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==",
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"darwin"
@@ -236,12 +284,13 @@
}
},
"node_modules/esbuild-freebsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz",
- "integrity": "sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz",
+ "integrity": "sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -251,12 +300,13 @@
}
},
"node_modules/esbuild-freebsd-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz",
- "integrity": "sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz",
+ "integrity": "sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==",
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -266,12 +316,13 @@
}
},
"node_modules/esbuild-linux-32": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz",
- "integrity": "sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz",
+ "integrity": "sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==",
"cpu": [
"ia32"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -281,12 +332,13 @@
}
},
"node_modules/esbuild-linux-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz",
- "integrity": "sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz",
+ "integrity": "sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -296,12 +348,13 @@
}
},
"node_modules/esbuild-linux-arm": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz",
- "integrity": "sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz",
+ "integrity": "sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==",
"cpu": [
"arm"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -311,12 +364,13 @@
}
},
"node_modules/esbuild-linux-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz",
- "integrity": "sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz",
+ "integrity": "sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==",
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -326,12 +380,13 @@
}
},
"node_modules/esbuild-linux-mips64le": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz",
- "integrity": "sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz",
+ "integrity": "sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==",
"cpu": [
"mips64el"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -341,12 +396,13 @@
}
},
"node_modules/esbuild-linux-ppc64le": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz",
- "integrity": "sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz",
+ "integrity": "sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==",
"cpu": [
"ppc64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -356,12 +412,13 @@
}
},
"node_modules/esbuild-linux-riscv64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz",
- "integrity": "sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz",
+ "integrity": "sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==",
"cpu": [
"riscv64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -371,12 +428,13 @@
}
},
"node_modules/esbuild-linux-s390x": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz",
- "integrity": "sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz",
+ "integrity": "sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==",
"cpu": [
"s390x"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -386,12 +444,13 @@
}
},
"node_modules/esbuild-netbsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz",
- "integrity": "sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz",
+ "integrity": "sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -401,12 +460,13 @@
}
},
"node_modules/esbuild-openbsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz",
- "integrity": "sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz",
+ "integrity": "sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -416,12 +476,13 @@
}
},
"node_modules/esbuild-sunos-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz",
- "integrity": "sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz",
+ "integrity": "sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"sunos"
@@ -431,12 +492,13 @@
}
},
"node_modules/esbuild-windows-32": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz",
- "integrity": "sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz",
+ "integrity": "sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==",
"cpu": [
"ia32"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -446,12 +508,13 @@
}
},
"node_modules/esbuild-windows-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz",
- "integrity": "sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz",
+ "integrity": "sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==",
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -461,12 +524,13 @@
}
},
"node_modules/esbuild-windows-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz",
- "integrity": "sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz",
+ "integrity": "sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==",
"cpu": [
"arm64"
],
+ "dev": true,
"optional": true,
"os": [
"win32"
@@ -480,6 +544,11 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
},
+ "node_modules/exenv": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+ "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -522,6 +591,22 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -540,6 +625,14 @@
"node": "*"
}
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -579,6 +672,69 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
"integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/react": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+ "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "scheduler": "^0.20.2"
+ },
+ "peerDependencies": {
+ "react": "17.0.2"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-modal": {
+ "version": "3.14.4",
+ "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz",
+ "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==",
+ "dependencies": {
+ "exenv": "^1.2.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.0",
+ "warning": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.0 || ^16 || ^17",
+ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17"
+ }
+ },
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -606,6 +762,15 @@
"npm": ">=3.10.0"
}
},
+ "node_modules/scheduler": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+ "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
"node_modules/sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
@@ -672,6 +837,14 @@
"integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==",
"dev": true
},
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/web-animations-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz",
@@ -747,9 +920,41 @@
"dev": true
},
"@types/node": {
- "version": "17.0.21",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
- "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
+ "version": "17.0.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+ "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+ "dev": true
+ },
+ "@types/prop-types": {
+ "version": "15.7.4",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+ "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==",
+ "dev": true
+ },
+ "@types/react": {
+ "version": "17.0.42",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.42.tgz",
+ "integrity": "sha512-nuab3x3CpJ7VFeNA+3HTUuEkvClYHXqWtWd7Ud6AZYW7Z3NH9WKtgU+tFB0ZLcHq+niB/HnzLcaZPqMJ95+k5Q==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/react-modal": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
+ "integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
"dev": true
},
"acorn": {
@@ -798,6 +1003,12 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
+ "csstype": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
+ "dev": true
+ },
"detect-browser": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz",
@@ -810,150 +1021,171 @@
"dev": true
},
"esbuild": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.25.tgz",
- "integrity": "sha512-4JHEIOMNFvK09ziiL+iVmldIhLbn49V4NAVo888tcGFKedEZY/Y8YapfStJ6zSE23tzYPKxqKwQBnQoIO0BI/Q==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.27.tgz",
+ "integrity": "sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==",
+ "dev": true,
"requires": {
- "esbuild-android-64": "0.14.25",
- "esbuild-android-arm64": "0.14.25",
- "esbuild-darwin-64": "0.14.25",
- "esbuild-darwin-arm64": "0.14.25",
- "esbuild-freebsd-64": "0.14.25",
- "esbuild-freebsd-arm64": "0.14.25",
- "esbuild-linux-32": "0.14.25",
- "esbuild-linux-64": "0.14.25",
- "esbuild-linux-arm": "0.14.25",
- "esbuild-linux-arm64": "0.14.25",
- "esbuild-linux-mips64le": "0.14.25",
- "esbuild-linux-ppc64le": "0.14.25",
- "esbuild-linux-riscv64": "0.14.25",
- "esbuild-linux-s390x": "0.14.25",
- "esbuild-netbsd-64": "0.14.25",
- "esbuild-openbsd-64": "0.14.25",
- "esbuild-sunos-64": "0.14.25",
- "esbuild-windows-32": "0.14.25",
- "esbuild-windows-64": "0.14.25",
- "esbuild-windows-arm64": "0.14.25"
+ "esbuild-android-64": "0.14.27",
+ "esbuild-android-arm64": "0.14.27",
+ "esbuild-darwin-64": "0.14.27",
+ "esbuild-darwin-arm64": "0.14.27",
+ "esbuild-freebsd-64": "0.14.27",
+ "esbuild-freebsd-arm64": "0.14.27",
+ "esbuild-linux-32": "0.14.27",
+ "esbuild-linux-64": "0.14.27",
+ "esbuild-linux-arm": "0.14.27",
+ "esbuild-linux-arm64": "0.14.27",
+ "esbuild-linux-mips64le": "0.14.27",
+ "esbuild-linux-ppc64le": "0.14.27",
+ "esbuild-linux-riscv64": "0.14.27",
+ "esbuild-linux-s390x": "0.14.27",
+ "esbuild-netbsd-64": "0.14.27",
+ "esbuild-openbsd-64": "0.14.27",
+ "esbuild-sunos-64": "0.14.27",
+ "esbuild-windows-32": "0.14.27",
+ "esbuild-windows-64": "0.14.27",
+ "esbuild-windows-arm64": "0.14.27"
}
},
"esbuild-android-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.25.tgz",
- "integrity": "sha512-L5vCUk7TzFbBnoESNoXjU3x9+/+7TDIE/1mTfy/erAfvZAqC+S3sp/Qa9wkypFMcFvN9FzvESkTlpeQDolREtQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz",
+ "integrity": "sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==",
+ "dev": true,
"optional": true
},
"esbuild-android-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.25.tgz",
- "integrity": "sha512-4jv5xPjM/qNm27T5j3ZEck0PvjgQtoMHnz4FzwF5zNP56PvY2CT0WStcAIl6jNlsuDdN63rk2HRBIsO6xFbcFw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz",
+ "integrity": "sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==",
+ "dev": true,
"optional": true
},
"esbuild-darwin-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.25.tgz",
- "integrity": "sha512-TGp8tuudIxOyWd1+8aYPxQmC1ZQyvij/AfNBa35RubixD0zJ1vkKHVAzo0Zao1zcG6pNqiSyzfPto8vmg0s7oA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz",
+ "integrity": "sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==",
+ "dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.25.tgz",
- "integrity": "sha512-oTcDgdm0MDVEmw2DWu8BV68pYuImpFgvWREPErBZmNA4MYKGuBRaCiJqq6jZmBR1x+3y1DWCjez+5uLtuAm6mw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz",
+ "integrity": "sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==",
+ "dev": true,
"optional": true
},
"esbuild-freebsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.25.tgz",
- "integrity": "sha512-ueAqbnMZ8arnuLH8tHwTCQYeptnHOUV7vA6px6j4zjjQwDx7TdP7kACPf3TLZLdJQ3CAD1XCvQ2sPhX+8tacvQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz",
+ "integrity": "sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==",
+ "dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.25.tgz",
- "integrity": "sha512-+ZVWud2HKh+Ob6k/qiJWjBtUg4KmJGGmbvEXXW1SNKS7hW7HU+Zq2ZCcE1akFxOPkVB+EhOty/sSek30tkCYug==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz",
+ "integrity": "sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==",
+ "dev": true,
"optional": true
},
"esbuild-linux-32": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.25.tgz",
- "integrity": "sha512-3OP/lwV3kCzEz45tobH9nj+uE4ubhGsfx+tn0L26WAGtUbmmcRpqy7XRG/qK7h1mClZ+eguIANcQntYMdYklfw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz",
+ "integrity": "sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==",
+ "dev": true,
"optional": true
},
"esbuild-linux-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.25.tgz",
- "integrity": "sha512-+aKHdHZmX9qwVlQmu5xYXh7GsBFf4TWrePgeJTalhXHOG7NNuUwoHmketGiZEoNsWyyqwH9rE5BC+iwcLY30Ug==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz",
+ "integrity": "sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==",
+ "dev": true,
"optional": true
},
"esbuild-linux-arm": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.25.tgz",
- "integrity": "sha512-aTLcE2VBoLydL943REcAcgnDi3bHtmULSXWLbjtBdtykRatJVSxKMjK9YlBXUZC4/YcNQfH7AxwVeQr9fNxPhw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz",
+ "integrity": "sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==",
+ "dev": true,
"optional": true
},
"esbuild-linux-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.25.tgz",
- "integrity": "sha512-UxfenPx/wSZx55gScCImPtXekvZQLI2GW3qe5dtlmU7luiqhp5GWPzGeQEbD3yN3xg/pHc671m5bma5Ns7lBHw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz",
+ "integrity": "sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==",
+ "dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.25.tgz",
- "integrity": "sha512-wLWYyqVfYx9Ur6eU5RT92yJVsaBGi5RdkoWqRHOqcJ38Kn60QMlcghsKeWfe9jcYut8LangYZ98xO1LxIoSXrQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz",
+ "integrity": "sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==",
+ "dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.25.tgz",
- "integrity": "sha512-0dR6Csl6Zas3g4p9ULckEl8Mo8IInJh33VCJ3eaV1hj9+MHGdmDOakYMN8MZP9/5nl+NU/0ygpd14cWgy8uqRw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz",
+ "integrity": "sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==",
+ "dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.25.tgz",
- "integrity": "sha512-J4d20HDmTrgvhR0bdkDhvvJGaikH3LzXQnNaseo8rcw9Yqby9A90gKUmWpfwqLVNRILvNnAmKLfBjCKU9ajg8w==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz",
+ "integrity": "sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==",
+ "dev": true,
"optional": true
},
"esbuild-linux-s390x": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.25.tgz",
- "integrity": "sha512-YI2d5V6nTE73ZnhEKQD7MtsPs1EtUZJ3obS21oxQxGbbRw1G+PtJKjNyur+3t6nzHP9oTg6GHQ3S3hOLLmbDIQ==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz",
+ "integrity": "sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==",
+ "dev": true,
"optional": true
},
"esbuild-netbsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.25.tgz",
- "integrity": "sha512-TKIVgNWLUOkr+Exrye70XTEE1lJjdQXdM4tAXRzfHE9iBA7LXWcNtVIuSnphTqpanPzTDFarF0yqq4kpbC6miA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz",
+ "integrity": "sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==",
+ "dev": true,
"optional": true
},
"esbuild-openbsd-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.25.tgz",
- "integrity": "sha512-QgFJ37A15D7NIXBTYEqz29+uw3nNBOIyog+3kFidANn6kjw0GHZ0lEYQn+cwjyzu94WobR+fes7cTl/ZYlHb1A==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz",
+ "integrity": "sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==",
+ "dev": true,
"optional": true
},
"esbuild-sunos-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.25.tgz",
- "integrity": "sha512-rmWfjUItYIVlqr5EnTH1+GCxXiBOC42WBZ3w++qh7n2cS9Xo0lO5pGSG2N+huOU2fX5L+6YUuJ78/vOYvefeFw==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz",
+ "integrity": "sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==",
+ "dev": true,
"optional": true
},
"esbuild-windows-32": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.25.tgz",
- "integrity": "sha512-HGAxVUofl3iUIz9W10Y9XKtD0bNsK9fBXv1D55N/ljNvkrAYcGB8YCm0v7DjlwtyS6ws3dkdQyXadbxkbzaKOA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz",
+ "integrity": "sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==",
+ "dev": true,
"optional": true
},
"esbuild-windows-64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.25.tgz",
- "integrity": "sha512-TirEohRkfWU9hXLgoDxzhMQD1g8I2mOqvdQF2RS9E/wbkORTAqJHyh7wqGRCQAwNzdNXdg3JAyhQ9/177AadWA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz",
+ "integrity": "sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==",
+ "dev": true,
"optional": true
},
"esbuild-windows-arm64": {
- "version": "0.14.25",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.25.tgz",
- "integrity": "sha512-4ype9ERiI45rSh+R8qUoBtaj6kJvUOI7oVLhKqPEpcF4Pa5PpT3hm/mXAyotJHREkHpM87PAJcA442mLnbtlNA==",
+ "version": "0.14.27",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz",
+ "integrity": "sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==",
+ "dev": true,
"optional": true
},
"eventemitter3": {
@@ -961,6 +1193,11 @@
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="
},
+ "exenv": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+ "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
+ },
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -997,6 +1234,19 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -1012,6 +1262,11 @@
"brace-expansion": "^1.1.7"
}
},
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1050,6 +1305,56 @@
"resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-1.0.1.tgz",
"integrity": "sha512-N6aeia3NhdpV7kiGxJV5xQiZZCVEEVjRz2T2C6UZQiBkHWHzUv/oWA4myQLcwBwO8LUoR1KWW5oStvwVesmfCg=="
},
+ "prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "react": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+ "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "scheduler": "^0.20.2"
+ }
+ },
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-modal": {
+ "version": "3.14.4",
+ "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.14.4.tgz",
+ "integrity": "sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg==",
+ "requires": {
+ "exenv": "^1.2.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.0",
+ "warning": "^4.0.3"
+ }
+ },
"rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -1067,6 +1372,15 @@
"sdp": "^2.6.0"
}
},
+ "scheduler": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+ "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
"sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
@@ -1105,6 +1419,14 @@
"integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==",
"dev": true
},
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"web-animations-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz",
diff --git a/package.json b/package.json
index 69750b8..8f56050 100644
--- a/package.json
+++ b/package.json
@@ -4,10 +4,9 @@
"description": "Dao (game)",
"main": "app.js",
"scripts": {
- "clean": "rimraf public/bundle.js",
- "build": "ts-node tools/esbuild.ts",
- "build-watch": "ts-node tools/esbuild-watch.ts",
- "start": "ts-node tools/esbuild-build-and-serve.ts",
+ "clean": "rimraf public/dist",
+ "build": "ts-node tools/build.ts",
+ "start": "ts-node tools/build-and-serve.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
@@ -22,12 +21,17 @@
"homepage": "https://gitlab.com/fuzzy-rp/dao-game#readme",
"dependencies": {
"detect-browser": "^5.3.0",
- "esbuild": "^0.14.25",
"peerjs": "^1.3.2",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-modal": "^3.14.4",
"web-animations-js": "^2.3.2"
},
"devDependencies": {
- "@types/node": "^17.0.21",
+ "@types/node": "^17.0.23",
+ "@types/react": "^17.0.42",
+ "@types/react-modal": "^3.13.1",
+ "esbuild": "^0.14.27",
"rimraf": "^3.0.2",
"ts-node": "^10.6.0",
"typescript": "^4.6.2"
diff --git a/public/index.html b/public/index.html
index adba5c1..dd30114 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="yin-yang.svg" type="image/svg+xml" />
<link rel="stylesheet" href="styles.css" />
- <script defer src="js/index.js"></script>
+ <script defer src="dist/index.js"></script>
</head>
<body>
diff --git a/public/local.html b/public/local.html
index 4c9952d..278cba7 100644
--- a/public/local.html
+++ b/public/local.html
@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="yin-yang.svg" type="image/svg+xml" />
<link rel="stylesheet" href="styles.css" />
- <script defer src="js/local.js"></script>
+ <script defer src="dist/pages/local.js"></script>
</head>
<body>
diff --git a/public/react.html b/public/react.html
new file mode 100644
index 0000000..829b0a5
--- /dev/null
+++ b/public/react.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8" />
+ <title>Dao Game in React</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="icon" href="yin-yang.svg" type="image/svg+xml" />
+ <script defer src="dist/app.js"></script>
+ <link rel="stylesheet" type="text/css" href="./dist/app.css" />
+</head>
+
+<body>
+ <div id="root"></div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/public/remote.html b/public/remote.html
index 4f57b0d..a509cd8 100644
--- a/public/remote.html
+++ b/public/remote.html
@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="yin-yang.svg" type="image/svg+xml" />
<link rel="stylesheet" href="styles.css" />
- <script defer src="js/remote.js"></script>
+ <script defer src="dist/pages/remote.js"></script>
</head>
<body>
diff --git a/readme.md b/readme.md
index 269c59e..8c5b737 100644
--- a/readme.md
+++ b/readme.md
@@ -30,3 +30,5 @@ This project is using [esbuild](https://esbuild.github.io/getting-started/#build
4. Add computer opponent using simple minimax logic.
1. [https://www.neverstopbuilding.com/blog/minimax](https://www.neverstopbuilding.com/blog/minimax)
2. [https://en.wikipedia.org/wiki/Minimax](https://en.wikipedia.org/wiki/Minimax)
+5. Add live reload support
+ 1. [https://how-to.dev/how-to-add-live-reload-to-esbuild-server](https://how-to.dev/how-to-add-live-reload-to-esbuild-server)
diff --git a/src/app.tsx b/src/app.tsx
new file mode 100644
index 0000000..c1f32fa
--- /dev/null
+++ b/src/app.tsx
@@ -0,0 +1,11 @@
+import React, { useCallback, useState } from 'react';
+import ReactDOM from 'react-dom';
+import { Game } from './game/game';
+import { App } from './react/App';
+
+
+const game = new Game('r', 'Red', 'b', 'Blue');
+const app = <App game={game} />
+const root = document.getElementById('root');
+
+ReactDOM.render(app, root);
diff --git a/src/game/coord.ts b/src/game/coord.ts
index ad5c5af..8111037 100644
--- a/src/game/coord.ts
+++ b/src/game/coord.ts
@@ -11,7 +11,7 @@ export class Coord {
return new Coord(this.row + coord.row, this.col + coord.col);
}
- sub(coord: Coord): Coord {
+ public sub(coord: Coord): Coord {
return new Coord(this.row - coord.row, this.col - coord.col);
}
@@ -19,20 +19,20 @@ export class Coord {
* Finds if otherCoord is contained within a rectangle that sits between origin and this Coord
* @param otherCoord
*/
- contains(otherCoord: Coord): boolean {
+ public contains(otherCoord: Coord): boolean {
const xRatio = otherCoord.col / this.col;
const yRatio = otherCoord.row / this.row;
const xyInsideSquare = xRatio >= 0 && xRatio < 1 && yRatio >= 0 && yRatio < 1;
return xyInsideSquare;
}
- equals(otherCoord: Coord): boolean {
+ public equals(otherCoord: Coord): boolean {
const areEqual = this.col === otherCoord.col && this.row === otherCoord.row;
return areEqual;
}
// clock face directions
- static get allDirections(): Coord[] {
+ public static get allDirections(): Coord[] {
const allDirections = [
new Coord(1, 0), // up
new Coord(1, 1), // up-right
diff --git a/src/game/game-board.ts b/src/game/game-board.ts
index dd1d14a..90544a6 100644
--- a/src/game/game-board.ts
+++ b/src/game/game-board.ts
@@ -1,42 +1,41 @@
import { Coord } from "./coord";
export class GameBoard {
- private board: (string | null)[];
+ public board: (string | null)[];
public readonly size: Coord;
public readonly player1Id: string;
public readonly player2Id: string;
public constructor(player1Id: string, player2Id: string) {
// default board
- const defaultBoard = []
- .concat([player1Id, null, null, player2Id])
- .concat([null, player1Id, player2Id, null])
- .concat([null, player2Id, player1Id, null])
- .concat([player2Id, null, null, player1Id]);
- const testBoard = []
- .concat([player1Id, null, player2Id, player2Id])
- .concat([player1Id, null, player2Id, null])
- .concat([player1Id, null, null, null])
- .concat([null, player1Id, null, player2Id]);
- this.board = testBoard;
+ const defaultBoard = ([] as (string | null)[]).concat.apply([], [
+ [player1Id, null, null, player2Id],
+ [null, player1Id, player2Id, null],
+ [null, player2Id, player1Id, null],
+ [player2Id, null, null, player1Id]]);
+ const testBoard = ([] as (string | null)[]).concat.apply([], [
+ [player1Id, null, player2Id, player2Id],
+ [player1Id, null, player2Id, null],
+ [player1Id, null, null, null],
+ [null, player1Id, null, player2Id]]);
+ this.board = defaultBoard;
this.size = new Coord(4, 4);
this.player1Id = player1Id;
this.player2Id = player2Id;
}
public move(source: Coord, dest: Coord): void {
- const isP1 = this.get(source);
- if (isP1 === null)
+ const piece = this.getPiece(source);
+ if (piece === null)
throw `Source ${source} points to an empty space`;
- this.set(source, null);
- this.set(dest, isP1);
+ this.setPiece(source, null);
+ this.setPiece(dest, piece);
}
- public get(coord: Coord): boolean | null {
+ public getPiece(coord: Coord): string | null {
const index = this.coordToBoardIndex(coord);
if (!Number.isSafeInteger(index) || index < 0 || index >= this.board.length)
throw `Index ${index} is out of bounds`;
const id = this.board[index];
- const isP1 = id === null ? null : id === this.player1Id;
- return isP1;
+ return id;
}
public getCoords(id: string): Coord[] {
const coords = this.board
@@ -44,21 +43,25 @@ export class GameBoard {
const isTarget = pieceId === id;
return isTarget ? this.boardIndexToCoord(index) : null;
})
- .filter((coord) => coord !== null);
+ .filter((coord) => coord !== null)
+ .map(coord => coord!);
return coords;
}
- private set(coord: Coord, isP1: boolean | null): void {
+ private setPiece(coord: Coord, piece: string | null): void {
+ const { player1Id, player2Id } = this;
+ if (piece !== null && piece !== player1Id && piece !== player2Id)
+ throw `Invalid piece specified: ${piece}`;
const index = this.coordToBoardIndex(coord);
if (!Number.isSafeInteger(index) || index < 0 || index >= this.board.length)
throw `Index ${index} is out of bounds`;
- this.board[index] = isP1 === null ? null : isP1 ? this.player1Id : this.player2Id;
+ this.board[index] = piece;
}
public coordToBoardIndex(coord: Coord): number {
const { row, col } = coord;
const index = row * this.size.col + col;
return index;
}
- private boardIndexToCoord(index: number): Coord {
+ public boardIndexToCoord(index: number): Coord {
const col = index % this.size.col;
const row = Math.floor(index / this.size.col);
return new Coord(row, col);
diff --git a/src/game/game.ts b/src/game/game.ts
index 2f4f2bc..c401292 100644
--- a/src/game/game.ts
+++ b/src/game/game.ts
@@ -26,7 +26,7 @@ export interface IGame {
getValidMoves: (start: Coord) => Coord[];
makeMove: (source: Coord, dest: Coord) => Promise<MoveResult>;
undoMove: () => Move;
- getPiece(pos: Coord): string | undefined;
+ getPiece(pos: Coord): string | null;
}
export class Game implements IGame {
@@ -48,11 +48,9 @@ export class Game implements IGame {
this._moveHistory = [];
this.currentPlayerId = player1Id;
}
- getPiece(pos: Coord): string {
- const value = this.gameBoard.get(pos);
- if (value === null) return null;
- const b = value.valueOf();
- const id = b ? this.player1Id : this.player2Id;
+ getPiece(pos: Coord): string | null {
+ const id = this.gameBoard.getPiece(pos);
+ if (id === null) return null;
return id;
}
public currentPlayerId: string;
@@ -126,6 +124,7 @@ export class Game implements IGame {
};
const { gameBoard, player1Id, player2Id } = this;
+
const isP1Turn = this.currentPlayerId === player1Id;
const playerId = isP1Turn ? player1Id : player2Id;
@@ -141,7 +140,7 @@ export class Game implements IGame {
return result;
}
public getValidMoves(start: Coord): Coord[] {
- const getFarthestCoord = (startCoord: Coord, direction: Coord): Coord => {
+ const getFarthestCoord = (startCoord: Coord, direction: Coord): Coord | undefined => {
const { gameBoard } = this;
let farthestPos = undefined;
let testPos = startCoord;
@@ -152,7 +151,7 @@ export class Game implements IGame {
if (!withinBounds) {
break;
}
- const isOccupied = gameBoard.get(pos) !== null;
+ const isOccupied = gameBoard.getPiece(pos) !== null;
if (isOccupied) {
break;
}
@@ -165,7 +164,8 @@ export class Game implements IGame {
const farthestCoords = Coord.allDirections
.map((direction) => getFarthestCoord(start, direction))
- .filter((farCoord) => farCoord !== undefined);
+ .filter((farCoord) => farCoord !== undefined)
+ .map(fc => fc!);
return farthestCoords;
}
@@ -174,7 +174,7 @@ export class Game implements IGame {
throw "Newp! Can't undo!";
}
const { gameBoard } = this;
- const move = this._moveHistory.pop();
+ const move = this._moveHistory.pop()!;
const { source, dest, playerId } = move;
this.currentPlayerId = playerId;
gameBoard.move(dest, source);
diff --git a/src/game/local-game.ts b/src/game/local-game.ts
index 33fb9c6..5462819 100644
--- a/src/game/local-game.ts
+++ b/src/game/local-game.ts
@@ -16,8 +16,8 @@ export class LocalGame extends Game {
// DOM references
readonly dom = {
- defaultP1Piece: document.getElementById('default-p1-piece'),
- defaultP2Piece: document.getElementById('default-p2-piece'),
+ defaultP1Piece: document.getElementById('default-p1-piece')!,
+ defaultP2Piece: document.getElementById('default-p2-piece')!,
resetButton: document.getElementById('reset-button') as HTMLInputElement,
undoButton: document.getElementById('undo-button') as HTMLInputElement,
cells: () => Array.from(document.querySelectorAll('td')),
@@ -83,7 +83,7 @@ export class LocalGame extends Game {
await Promise.all([sourceAnimation.finished, destAnimation.finished]);
// remove old piece
- piece.parentNode.removeChild(piece);
+ piece.parentNode!.removeChild(piece);
}
public constructor(player1Id: string, player1Name: string, player2Id: string, player2Name: string) {
@@ -196,7 +196,7 @@ export class LocalGame extends Game {
// create board and pieces
let isWhite = true;
- const table = document.getElementById('table');
+ const table = document.getElementById('table')!;
for (let r = 0; r < size.row; r++) {
const tr = document.createElement('tr');
table.appendChild(tr);
diff --git a/src/game/remote-game.ts b/src/game/remote-game.ts
index 0549c28..086e498 100644
--- a/src/game/remote-game.ts
+++ b/src/game/remote-game.ts
@@ -11,8 +11,8 @@ import { copyToClipboard } from '../utils/clipboard';
import { detect } from 'detect-browser';
interface LocalData {
- id: string;
- peerId: string;
+ id?: string;
+ peerId?: string;
}
interface Pos {
x: number;
@@ -24,9 +24,9 @@ interface PeerData {
}
const peerOptions: PeerJSOption = { debug: 0 };
const { getter: getData, setter: setData } = useSessionStorage<LocalData>('dao-game', { id: undefined, peerId: undefined });
-const otherMouseElement = document.getElementById('other-mouse');
-const shareContainer = document.getElementById('share-container');
-const shareLinkButton = document.getElementById('share-link');
+const otherMouseElement = document.getElementById('other-mouse')!;
+const shareContainer = document.getElementById('share-container')!;
+const shareLinkButton = document.getElementById('share-link')!;
const minString = (a: string, b: string): string => (a < b ? a : b);
const maxString = (a: string, b: string): string => (a > b ? a : b);
const getFirstPlayerId = (playerId1: string, playerId2: string) => {
@@ -42,7 +42,7 @@ const dropFileExtension = (path: string) => {
const dirName = path.substring(0, lastSlashIndex);
return dirName;
}
-const showMessageAndLeave = async (title, text) => {
+const showMessageAndLeave = async (title: string, text: string) => {
await showModal({ title, text, willBackgroundCancel: false });
sessionStorage.clear();
const { protocol, host, pathname } = location;
@@ -76,7 +76,7 @@ export class PeerGame extends LocalGame {
public override async handleMoveResult(result: MoveResult): Promise<void> {
const { isTerminal, message } = result;
if (isTerminal) {
- await showMessageAndLeave('Game Over', message);
+ await showMessageAndLeave('Game Over', message!);
} else {
this.resetUi();
this.setActiveUi();
@@ -86,7 +86,7 @@ export class PeerGame extends LocalGame {
public static async createPeerGame(): Promise<PeerGame> {
let { id, peerId } = getData();
const main = document.getElementsByTagName('main')[0];
- let game: PeerGame = undefined;
+ let game: PeerGame | undefined = undefined;
const handleConn = async (conn: Peer.DataConnection) => {
shareContainer.classList.add('invisible');
@@ -107,7 +107,7 @@ export class PeerGame extends LocalGame {
game.setActiveUi();
main.classList.remove('invisible');
- const table = document.getElementById('table');
+ const table = document.getElementById('table')!;
table.addEventListener('mousemove', (ev: MouseEvent) => {
const xTable = ev.clientX - table.offsetLeft;
const yTable = ev.clientY - table.offsetTop;
@@ -121,8 +121,10 @@ export class PeerGame extends LocalGame {
conn.send(JSON.stringify(peerData));
});
- let lastTimerId = undefined;
+ let lastTimerId: NodeJS.Timeout | undefined = undefined;
conn.on('data', (data: string) => {
+ if (!game) return;
+
const peerData = JSON.parse(data) as PeerData;
const { mousePos, move } = peerData;
diff --git a/src/react/App.css b/src/react/App.css
new file mode 100644
index 0000000..1e0e319
--- /dev/null
+++ b/src/react/App.css
@@ -0,0 +1,25 @@
+@import url('./Base.css');
+
+.grid-container {
+ display: grid;
+ grid-template-areas:
+ '0 1 2 3'
+ '4 5 6 7'
+ '8 9 10 11'
+ '12 13 14 15';
+ width: calc(var(--cell-size) * 4);
+ height: calc(var(--cell-size) * 4);
+ margin: auto;
+}
+
+.modal {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: xx-large;
+ /* margin: 100px 100px 100px 100px; */
+ margin: auto;
+ text-align: center;
+ background-color: white;
+ user-select: none;
+ border: 1px solid black;
+ padding: 20px;
+}
diff --git a/src/react/App.tsx b/src/react/App.tsx
new file mode 100644
index 0000000..1218741
--- /dev/null
+++ b/src/react/App.tsx
@@ -0,0 +1,111 @@
+import * as React from 'react';
+import { useEffect, useState } from 'react';
+import { Coord } from '../game/coord';
+import { IGame, MoveResult } from '../game/game';
+import Modal from 'react-modal';
+import './App.css';
+import { Tile } from './Tile';
+import { Controls } from './Controls';
+import { delay } from '../utils/delay';
+import { Status } from './Status';
+
+export type Props = {
+ game: IGame;
+};
+
+export const App = (props: Props): React.ReactElement => {
+ const { game } = props;
+ const [selectedPiecePos, setSelectedPiecePos] = useState<Coord>()
+ const [validMoves, setValidMoves] = useState<Coord[]>([]);
+ const [currentPlayerId, setCurrentPlayerId] = useState<string>(game.currentPlayerId);
+ const [moveResult, setMoveResult] = useState<MoveResult>();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isControlsDisabled, setIsControlsDisabled] = useState(!game.canUndoMove);
+
+ useEffect((): void => {
+ const slowUpdate = async () => {
+ if (moveResult && moveResult.isTerminal) {
+ await delay(750);
+ setIsModalOpen(true);
+ }
+ };
+ slowUpdate();
+ }, [moveResult]);
+
+ const makeMove = async (from: Coord, to: Coord) => {
+ const result = await game.makeMove(from, to);
+ setCurrentPlayerId(game.currentPlayerId);
+ setSelectedPiecePos(undefined);
+ setValidMoves([]);
+ setMoveResult(result);
+ setIsControlsDisabled(false);
+ };
+ const undoMove = () => {
+ const move = game.undoMove();
+ console.debug({ move });
+ setCurrentPlayerId(game.currentPlayerId);
+ setSelectedPiecePos(undefined);
+ setValidMoves([]);
+ setMoveResult(undefined);
+ setIsControlsDisabled(!game.canUndoMove);
+ };
+
+ const allTiles: React.ReactElement[] = [];
+ for (let r = 0; r < game.size.row; r++) {
+ for (let c = 0; c < game.size.col; c++) {
+ const pos = new Coord(r, c);
+ const pieceClickCb = () => {
+ const isUnselectClick = selectedPiecePos !== undefined && selectedPiecePos.equals(pos);
+ if (isUnselectClick) {
+ setSelectedPiecePos(undefined);
+ setValidMoves([]);
+ } else {
+ setSelectedPiecePos(pos);
+ setValidMoves([...game.getValidMoves(pos)]);
+ }
+ };
+ const validMoveClickCb = async () => {
+ await makeMove(selectedPiecePos!, pos)
+ };
+ const tile =
+ <Tile
+ pos={pos}
+ game={game}
+ validMoves={validMoves}
+ selectedPiecePos={selectedPiecePos}
+ currentPlayerId={currentPlayerId}
+ pieceClickCb={() => pieceClickCb()}
+ validMoveClickCb={() => validMoveClickCb()}
+ key={game.coordToBoardIndex(pos)}
+ />;
+ allTiles.push(tile);
+ }
+ }
+
+ return (
+ <div>
+ <Status
+ currentPlayerId={currentPlayerId}
+ player1Id={game.player1Id}
+ player1Name={game.player1Name}
+ player2Id={game.player2Id}
+ player2Name={game.player2Name}
+ />
+ <div className='grid-container'>
+ {allTiles}
+ </div>
+ <Controls onUndo={undoMove} isDisabled={isControlsDisabled} />
+ <Modal isOpen={isModalOpen} className='modal'>
+ <h2>Game Over</h2>
+ <div>
+ {moveResult?.message}
+ </div>
+ <div>
+ <button onClick={() => location.reload()}>
+ Restart game
+ </button>
+ </div>
+ </Modal>
+ </div>
+ );
+}
\ No newline at end of file
diff --git a/src/react/Base.css b/src/react/Base.css
new file mode 100644
index 0000000..f76082d
--- /dev/null
+++ b/src/react/Base.css
@@ -0,0 +1,29 @@
+:root {
+ --default-size: 5px;
+ --active-size: 40px;
+ --active-pulse-size: 30px;
+ --cell-size: 80px;
+ --max-width: 500px;
+ --font-family: Arial, sans-serif;
+}
+
+button {
+ border-radius: 10px;
+ padding: 10px;
+ background-color: orange;
+ font-size: xx-large;
+ border-color: transparent;
+ transition: 0.2s all;
+ margin: 30px;
+}
+button:hover:enabled {
+ transform: scale(1.02);
+ box-shadow: 3px 2px 22px 1px rgba(0, 0, 0, 0.24);
+}
+button:active:enabled {
+ transform: scale(0.98);
+ box-shadow: 3px 2px 22px 1px rgba(0, 0, 0, 0.24);
+}
+button:disabled {
+ background-color: gainsboro;
+}
\ No newline at end of file
diff --git a/src/react/Controls.css b/src/react/Controls.css
new file mode 100644
index 0000000..9c584fe
--- /dev/null
+++ b/src/react/Controls.css
@@ -0,0 +1,31 @@
+@import url('./Base.css');
+
+.container {
+ margin: auto;
+ padding: 50px;
+}
+
+.controls {
+ font-size: 90px;
+ font-family: var(--font-family);
+ font-size: 55px;
+ border-radius: 10px;
+ padding: 10px;
+ max-width: var(--max-width);
+ display: flex;
+ margin: auto;
+ /* box-sizing: border-box; */
+ /* text-align: center; */
+}
+
+.undo-button {
+ /* display: inline-block; */
+ float: left;
+ margin: auto;
+}
+
+.reset-button {
+ /* display: inline-block; */
+ float: right;
+ margin: auto;
+}
diff --git a/src/react/Controls.tsx b/src/react/Controls.tsx
new file mode 100644
index 0000000..b878f6c
--- /dev/null
+++ b/src/react/Controls.tsx
@@ -0,0 +1,27 @@
+import * as React from 'react';
+import './Controls.css';
+
+export type Props = {
+ isDisabled: boolean
+ onUndo: () => void
+};
+
+const reload = () => location.reload();
+
+export const Controls = (props: Props): React.ReactElement => {
+ const { isDisabled, onUndo } = props;
+ return <div className='container'>
+ <div className='controls'>
+ <button
+ disabled={isDisabled}
+ type='button'
+ onClick={onUndo}
+ className='undo-button'>Undo</button>
+ <button
+ disabled={isDisabled}
+ type='button'
+ onClick={reload}
+ className='reset-button'>Reset</button>
+ </div>
+ </div>;
+}
\ No newline at end of file
diff --git a/src/react/Piece.css b/src/react/Piece.css
new file mode 100644
index 0000000..2b97b9f
--- /dev/null
+++ b/src/react/Piece.css
@@ -0,0 +1,51 @@
+@import url('./Base.css');
+
+.piece {
+ width: var(--default-size);
+ height: var(--default-size);
+ padding: 10px;
+ border-radius: 50px;
+ animation: activeToDisabled .5s 1;
+}
+
+.p1-piece {
+ background-color: crimson;
+}
+.p2-piece {
+ background-color: mediumblue;
+}
+
+.active-player-piece {
+ width: var(--active-size);
+ height: var(--active-size);
+ animation: disabledToActive .5s 1;
+}
+.active-player-piece:hover {
+ filter: drop-shadow(1px 1px 5px black);
+}
+
+.selected-piece {
+ border: 2px white solid;
+ filter: drop-shadow(1px 1px 5px black);
+}
+
+@keyframes disabledToActive {
+ from {
+ width: var(--default-size);
+ height: var(--default-size);
+ }
+ to {
+ width: var(--active-size);
+ height: var(--active-size);
+ }
+}
+@keyframes activeToDisabled {
+ from {
+ width: var(--active-size);
+ height: var(--active-size);
+ }
+ to {
+ width: var(--default-size);
+ height: var(--default-size);
+ }
+}
diff --git a/src/react/Piece.tsx b/src/react/Piece.tsx
new file mode 100644
index 0000000..af54f4b
--- /dev/null
+++ b/src/react/Piece.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import './Piece.css';
+
+export type Props = {
+ isPlayer1: boolean,
+ isCurrentPlayer: boolean,
+ isSelectedPiece: boolean,
+ clickCb: () => void
+}
+export const Piece = (props: Props): React.ReactElement => {
+ const { isPlayer1, isCurrentPlayer, isSelectedPiece } = props;
+ const classNames = [
+ 'piece',
+ isPlayer1 ? 'p1-piece' : 'p2-piece',
+ isCurrentPlayer && 'active-player-piece',
+ isSelectedPiece && 'selected-piece'
+ ];
+
+ return (
+ <div className={classNames.join(' ')} onClick={props.clickCb} />
+ )
+};
diff --git a/src/react/Status.css b/src/react/Status.css
new file mode 100644
index 0000000..0572354
--- /dev/null
+++ b/src/react/Status.css
@@ -0,0 +1,34 @@
+@import url('./Base.css');
+
+.status {
+ font-family: var(--font-family);
+ font-size: 55px;
+ border-radius: 10px;
+ padding: 10px;
+ max-width: var(--max-width);
+ display: flex;
+ margin: auto;
+}
+
+.p1-turn,
+.p2-turn {
+ text-align: center;
+ color: lightgray;
+ background-color: white;
+ border-radius: 10px;
+ padding: 10px;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ margin: auto;
+}
+
+.active {
+ color: white;
+}
+.p1-turn.active {
+ background-color: red;
+}
+.p2-turn.active {
+ background-color: blue;
+}
\ No newline at end of file
diff --git a/src/react/Status.tsx b/src/react/Status.tsx
new file mode 100644
index 0000000..1f3ef5a
--- /dev/null
+++ b/src/react/Status.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import './Status.css';
+
+export type Props = {
+ currentPlayerId: string
+ player1Id: string
+ player1Name: string
+ player2Id: string
+ player2Name: string
+};
+export const Status = (props: Props): React.ReactElement => {
+
+ const { currentPlayerId, player1Id, player1Name, player2Name } = props;
+ const isPlayer1 = currentPlayerId === player1Id;
+
+ return (
+ <div className='status'>
+ <span className={`p1-turn ${isPlayer1 ? 'active' : ''}`}>{player1Name} turn</span>
+ <span className={`p2-turn ${isPlayer1 ? '' : 'active'}`}>{player2Name} turn</span>
+ </div>
+ );
+};
diff --git a/src/react/Tile.css b/src/react/Tile.css
new file mode 100644
index 0000000..166b474
--- /dev/null
+++ b/src/react/Tile.css
@@ -0,0 +1,32 @@
+@import url('./Base.css');
+
+.tile {
+ width: var(--cell-size);
+ height: var(--cell-size);
+ text-align: center;
+ border: 2px transparent solid;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: auto;
+}
+
+.white {
+ background-color: orange;
+}
+
+.black {
+ background-color: wheat;
+}
+
+/* .valid-move {
+ border-color: black;
+ filter: drop-shadow(0px 0px 5px black);
+} */
+
+.possible-move {
+ animation: blinkingText 1s infinite;
+ border-color: black;
+ filter: drop-shadow(0px 0px 5px green);
+}
diff --git a/src/react/Tile.tsx b/src/react/Tile.tsx
new file mode 100644
index 0000000..2229a15
--- /dev/null
+++ b/src/react/Tile.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react';
+import { Coord } from '../game/coord';
+import { IGame } from '../game/game';
+import { Piece } from './Piece';
+import './Tile.css';
+
+export type Props = {
+ pos: Coord,
+ game: IGame,
+ validMoves: Coord[],
+ selectedPiecePos: Coord | undefined,
+ currentPlayerId: string,
+ pieceClickCb: () => void,
+ validMoveClickCb: () => void
+};
+export const Tile = (props: Props): React.ReactElement => {
+
+ const { pos, game, validMoves, selectedPiecePos, currentPlayerId } = props;
+ const { player1Id } = game;
+ const { row, col } = pos;
+
+ const isWhite = row % 2 === col % 2;
+ const piece = game.getPiece(pos);
+ const isPlayer1 = piece === player1Id;
+ const isCurrentPlayer = piece === currentPlayerId;
+ const isSelectedPiece = selectedPiecePos !== undefined && pos.equals(selectedPiecePos);
+ const isValidMove = validMoves.findIndex(vm => vm.equals(pos)) >= 0;
+ const classNames = [
+ 'tile',
+ isWhite ? 'white' : 'black',
+ isValidMove && 'possible-move'
+ ];
+ return (
+ <div
+ className={classNames.join(' ')}
+ onClick={() => { if (isValidMove) { props.validMoveClickCb(); } }}>
+ {(piece !== null) &&
+ <Piece
+ isPlayer1={isPlayer1}
+ isCurrentPlayer={isCurrentPlayer}
+ clickCb={() => { if (isCurrentPlayer) { props.pieceClickCb() } }}
+ isSelectedPiece={isSelectedPiece}
+ />}
+ </div>
+ );
+};
diff --git a/tools/esbuild-build-and-serve.ts b/tools/build-and-serve.ts
similarity index 97%
rename from tools/esbuild-build-and-serve.ts
rename to tools/build-and-serve.ts
index d2fa260..543f4c6 100644
--- a/tools/esbuild-build-and-serve.ts
+++ b/tools/build-and-serve.ts
@@ -1,6 +1,6 @@
import esbuild from 'esbuild';
import http from 'http';
-import { serveOptions, buildOptions, hostPort, clearOutDir } from './esbuild-settings'
+import { serveOptions, buildOptions, hostPort, clearOutDir } from './settings'
clearOutDir();
diff --git a/tools/build.ts b/tools/build.ts
new file mode 100644
index 0000000..d8857df
--- /dev/null
+++ b/tools/build.ts
@@ -0,0 +1,6 @@
+import esbuild from 'esbuild';
+import { buildOptions, clearOutDir } from './settings'
+
+clearOutDir();
+
+esbuild.build(buildOptions).catch(() => process.exit(1))
\ No newline at end of file
diff --git a/tools/esbuild-watch.ts b/tools/esbuild-watch.ts
deleted file mode 100644
index 49a3d03..0000000
--- a/tools/esbuild-watch.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import esbuild from 'esbuild';
-import { buildOptions, clearOutDir } from './esbuild-settings'
-
-clearOutDir();
-
-esbuild.build(buildOptions).catch(() => process.exit(1))
\ No newline at end of file
diff --git a/tools/esbuild.ts b/tools/esbuild.ts
deleted file mode 100644
index b02895b..0000000
--- a/tools/esbuild.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import esbuild from 'esbuild';
-import { serveOptions, buildOptions, hostPort, clearOutDir } from './esbuild-settings'
-
-clearOutDir();
-
-esbuild.build(buildOptions).catch(() => process.exit(1))
\ No newline at end of file
diff --git a/tools/esbuild-settings.ts b/tools/settings.ts
similarity index 79%
rename from tools/esbuild-settings.ts
rename to tools/settings.ts
index 48eb1af..57b3fd7 100644
--- a/tools/esbuild-settings.ts
+++ b/tools/settings.ts
@@ -1,24 +1,22 @@
import esbuild from 'esbuild';
-import fs from 'fs'
+import fs from 'fs';
export const hostPort = 3000;
-const outDir = 'public/js';
+const outDir = 'public/dist';
export const serveOptions: esbuild.ServeOptions = {
servedir: 'public',
};
export const buildOptions: esbuild.BuildOptions = {
- entryPoints: ['src/pages/local.ts', 'src/pages/remote.ts', 'src/pages/index.ts'],
- bundle: true,
+ logLevel: "info",
+ entryPoints: ['src/pages/local.ts', 'src/pages/remote.ts', 'src/pages/index.ts', 'src/app.tsx'],
outdir: outDir,
+ bundle: true,
sourcemap: true,
target: [
'es5',
'es2016'
- ],
- loader: {
- '.ts': 'ts'
- }
+ ]
};
export function clearOutDir() {
fs.rmSync(outDir, { recursive: true, force: true });
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..e33beee
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,93 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig.json to read more about this file */
+ /* Projects */
+ // "incremental": true, /* Enable incremental compilation */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ /* Language and Environment */
+ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ "jsx": "react", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
+ // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ /* Modules */
+ "module": "commonjs", /* Specify what module code is generated. */
+ "rootDir": "src", /* Specify the root folder within your source files. */
+ // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "resolveJsonModule": true, /* Enable importing .json files */
+ // "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+ /* Type Checking */
+ "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
+ // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
+ // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
\ No newline at end of file