board-game-engine-react 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/webpack.config.js +1 -1
- package/babel.config.js +2 -1
- package/dist/board-game-engine-react.cjs +100 -90
- package/dist/board-game-engine-react.mjs +103 -78
- package/dist/components/abstract-choices/abstract-choices.d.ts +2 -0
- package/dist/components/abstract-choices/abstract-choices.d.ts.map +1 -0
- package/dist/components/board/grid.d.ts +5 -0
- package/dist/components/board/grid.d.ts.map +1 -0
- package/dist/components/entity/entity.d.ts +5 -0
- package/dist/components/entity/entity.d.ts.map +1 -0
- package/dist/components/game/game.d.ts +8 -0
- package/dist/components/game/game.d.ts.map +1 -0
- package/dist/components/game-status/game-status.d.ts +5 -0
- package/dist/components/game-status/game-status.d.ts.map +1 -0
- package/dist/components/space/space.d.ts +5 -0
- package/dist/components/space/space.d.ts.map +1 -0
- package/dist/contexts/game-context.d.ts +15 -0
- package/dist/contexts/game-context.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/use-gameserver-connection.d.ts +8 -0
- package/dist/use-gameserver-connection.d.ts.map +1 -0
- package/examples/checkers.json +2 -2
- package/examples/connect-four.json +1 -1
- package/examples/eights.json +15 -15
- package/examples/index.html +219 -0
- package/package.json +12 -10
- package/scripts/build.mjs +3 -3
- package/src/components/abstract-choices/{abstract-choices.js → abstract-choices.tsx} +12 -8
- package/src/components/board/grid.tsx +29 -0
- package/src/components/entity/entity.tsx +40 -0
- package/src/components/game/{game.js → game.tsx} +22 -9
- package/src/components/game-status/game-status.tsx +30 -0
- package/src/components/space/space.tsx +52 -0
- package/src/contexts/game-context.tsx +60 -0
- package/src/global.d.ts +1 -0
- package/src/index.ts +8 -0
- package/src/types.ts +25 -0
- package/src/use-gameserver-connection.ts +82 -0
- package/stories/BoardGameEngineReact.stories.tsx +85 -0
- package/test-app/index.html +1 -1
- package/test-app/{main.jsx → main.tsx} +13 -11
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +34 -0
- package/board-game-engine-react.test.js +0 -12
- package/jest.config.js +0 -13
- package/src/components/board/grid.js +0 -28
- package/src/components/entity/entity.js +0 -38
- package/src/components/game-status/game-status.js +0 -20
- package/src/components/space/space.js +0 -68
- package/src/contexts/game-context.js +0 -35
- package/src/index.js +0 -5
- package/src/use-gameserver-connection.js +0 -74
- package/stories/BoardGameEngineReact.stories.js +0 -72
|
@@ -7,7 +7,7 @@ const modulesToTranspile = [
|
|
|
7
7
|
module.exports = ({ config, mode }) => {
|
|
8
8
|
config.resolve.alias['react-yarn-visual-novel'] = mode === 'PRODUCTION'
|
|
9
9
|
? path.resolve(__dirname, '../dist/react-yarn-visual-novel.min.js')
|
|
10
|
-
: path.resolve(__dirname, '../src/index.
|
|
10
|
+
: path.resolve(__dirname, '../src/index.ts')
|
|
11
11
|
|
|
12
12
|
// brittle
|
|
13
13
|
const babelLoader = config.module.rules[config.module.rules.length - 1]
|
package/babel.config.js
CHANGED
|
@@ -2,13 +2,14 @@ const coverage = process.env.COVERAGE === '1'
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
presets: [
|
|
5
|
+
'@babel/preset-typescript',
|
|
5
6
|
'@babel/preset-react',
|
|
6
7
|
['@babel/preset-env', { targets: { node: 'current' } }]
|
|
7
8
|
],
|
|
8
9
|
plugins: [
|
|
9
10
|
'@babel/plugin-proposal-object-rest-spread',
|
|
10
11
|
...(coverage
|
|
11
|
-
? [['istanbul', { include: ['src/**/*.
|
|
12
|
+
? [['istanbul', { include: ['src/**/*.ts', 'src/**/*.tsx'], exclude: ['**/*.spec.js', '**/node_modules/**', 'e2e/**'] }]]
|
|
12
13
|
: [])
|
|
13
14
|
]
|
|
14
15
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
6
|
var __export = (target, all) => {
|
|
8
7
|
for (var name in all)
|
|
@@ -16,17 +15,9 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
15
|
}
|
|
17
16
|
return to;
|
|
18
17
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
19
|
|
|
29
|
-
// src/index.
|
|
20
|
+
// src/index.ts
|
|
30
21
|
var index_exports = {};
|
|
31
22
|
__export(index_exports, {
|
|
32
23
|
Client: () => import_board_game_engine2.Client,
|
|
@@ -37,45 +28,55 @@ __export(index_exports, {
|
|
|
37
28
|
module.exports = __toCommonJS(index_exports);
|
|
38
29
|
var import_board_game_engine2 = require("board-game-engine");
|
|
39
30
|
|
|
40
|
-
// src/
|
|
41
|
-
var
|
|
42
|
-
|
|
43
|
-
// src/components/entity/entity.js
|
|
44
|
-
var import_react4 = __toESM(require("react"));
|
|
45
|
-
|
|
46
|
-
// src/contexts/game-context.js
|
|
47
|
-
var import_react = __toESM(require("react"));
|
|
31
|
+
// src/contexts/game-context.tsx
|
|
32
|
+
var import_react = require("react");
|
|
48
33
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
49
34
|
var GameContext = (0, import_react.createContext)({
|
|
50
35
|
clickTarget: () => {
|
|
51
|
-
}
|
|
36
|
+
},
|
|
37
|
+
undoStep: () => {
|
|
38
|
+
},
|
|
39
|
+
allClickable: /* @__PURE__ */ new Set(),
|
|
40
|
+
currentMoveTargets: []
|
|
52
41
|
});
|
|
53
|
-
function GameProvider({
|
|
42
|
+
function GameProvider({
|
|
43
|
+
gameConnection,
|
|
44
|
+
children,
|
|
45
|
+
isSpectator
|
|
46
|
+
}) {
|
|
54
47
|
(0, import_react.useEffect)(() => {
|
|
55
|
-
if (gameConnection.state
|
|
48
|
+
if (gameConnection.state?._stateID === 0) {
|
|
56
49
|
gameConnection.reset();
|
|
57
50
|
}
|
|
58
|
-
}, [gameConnection.state
|
|
59
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
}, [gameConnection.state?._stateID]);
|
|
52
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53
|
+
GameContext.Provider,
|
|
54
|
+
{
|
|
55
|
+
value: {
|
|
56
|
+
clickTarget: (target) => {
|
|
57
|
+
if (!isSpectator) {
|
|
58
|
+
gameConnection.doStep(target);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
undoStep: () => {
|
|
62
|
+
gameConnection.undoStep();
|
|
63
|
+
},
|
|
64
|
+
allClickable: gameConnection.optimisticWinner || isSpectator ? /* @__PURE__ */ new Set() : gameConnection.allClickable ?? /* @__PURE__ */ new Set(),
|
|
65
|
+
currentMoveTargets: gameConnection.optimisticWinner || isSpectator ? [] : gameConnection.moveBuilder?.targets ?? []
|
|
66
|
+
},
|
|
67
|
+
children
|
|
68
|
+
}
|
|
69
|
+
);
|
|
71
70
|
}
|
|
72
71
|
var useGame = () => (0, import_react.useContext)(GameContext);
|
|
73
72
|
|
|
74
|
-
// src/components/board/grid.
|
|
75
|
-
var import_react2 = __toESM(require("react"));
|
|
73
|
+
// src/components/board/grid.tsx
|
|
76
74
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
77
75
|
function Grid({ grid }) {
|
|
78
76
|
const { width, height, spaces } = grid.attributes;
|
|
77
|
+
const w = Number(width ?? 0);
|
|
78
|
+
const h = Number(height ?? 0);
|
|
79
|
+
const spaceList = spaces ?? [];
|
|
79
80
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
80
81
|
"div",
|
|
81
82
|
{
|
|
@@ -83,31 +84,29 @@ function Grid({ grid }) {
|
|
|
83
84
|
style: {
|
|
84
85
|
display: "inline-grid",
|
|
85
86
|
width: "100%",
|
|
86
|
-
gridTemplateColumns: `repeat(${
|
|
87
|
-
gridTemplateRows: `repeat(${
|
|
87
|
+
gridTemplateColumns: `repeat(${w}, 1fr)`,
|
|
88
|
+
gridTemplateRows: `repeat(${h}, 1fr)`
|
|
88
89
|
},
|
|
89
|
-
children:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
);
|
|
98
|
-
})
|
|
90
|
+
children: spaceList.map((space, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
className: "grid__cell",
|
|
94
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Entity, { entity: space })
|
|
95
|
+
},
|
|
96
|
+
index
|
|
97
|
+
))
|
|
99
98
|
}
|
|
100
99
|
);
|
|
101
100
|
}
|
|
102
101
|
|
|
103
|
-
// src/components/space/space.
|
|
104
|
-
var import_react3 = __toESM(require("react"));
|
|
102
|
+
// src/components/space/space.tsx
|
|
105
103
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
106
104
|
function Space({ space }) {
|
|
107
105
|
const { clickTarget, allClickable, currentMoveTargets } = useGame();
|
|
108
106
|
const { entities, entityId } = space.attributes;
|
|
107
|
+
const entityList = entities ?? [];
|
|
109
108
|
const clickable = allClickable.has(space);
|
|
110
|
-
const targeted = currentMoveTargets
|
|
109
|
+
const targeted = currentMoveTargets.map((e) => e.entityId ?? e.attributes?.entityId).includes(entityId);
|
|
111
110
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
112
111
|
"a",
|
|
113
112
|
{
|
|
@@ -132,18 +131,18 @@ function Space({ space }) {
|
|
|
132
131
|
flexWrap: "wrap"
|
|
133
132
|
},
|
|
134
133
|
children: [
|
|
135
|
-
Array.from({ length:
|
|
134
|
+
Array.from({ length: entityList.length }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
136
135
|
"div",
|
|
137
136
|
{
|
|
138
137
|
className: "space__entity-grid__cell",
|
|
139
138
|
style: {
|
|
140
139
|
display: "inline-block"
|
|
141
140
|
},
|
|
142
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Entity, { entity:
|
|
141
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Entity, { entity: entityList[i] })
|
|
143
142
|
},
|
|
144
143
|
i
|
|
145
144
|
)),
|
|
146
|
-
!
|
|
145
|
+
!entityList.length && String(space.attributes.name ?? "")
|
|
147
146
|
]
|
|
148
147
|
}
|
|
149
148
|
)
|
|
@@ -151,7 +150,7 @@ function Space({ space }) {
|
|
|
151
150
|
);
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
// src/components/entity/entity.
|
|
153
|
+
// src/components/entity/entity.tsx
|
|
155
154
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
156
155
|
function Entity({ entity }) {
|
|
157
156
|
const { clickTarget, allClickable } = useGame();
|
|
@@ -160,9 +159,9 @@ function Entity({ entity }) {
|
|
|
160
159
|
const entityType = attributes.entityType ?? attributes.type;
|
|
161
160
|
switch (entityType) {
|
|
162
161
|
case "Grid":
|
|
163
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Grid, { grid: entity
|
|
162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Grid, { grid: entity });
|
|
164
163
|
case "Space":
|
|
165
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Space, { space: entity
|
|
164
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Space, { space: entity });
|
|
166
165
|
default:
|
|
167
166
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
168
167
|
"div",
|
|
@@ -175,31 +174,33 @@ function Entity({ entity }) {
|
|
|
175
174
|
},
|
|
176
175
|
className: [
|
|
177
176
|
"entity",
|
|
178
|
-
attributes.player && `player-${attributes.player}`,
|
|
177
|
+
attributes.player !== void 0 && `player-${String(attributes.player)}`,
|
|
179
178
|
isClickable && "entity--clickable"
|
|
180
179
|
].filter(Boolean).join(" "),
|
|
181
180
|
children: entity.rule.displayProperties?.map((property, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
182
181
|
property,
|
|
183
182
|
": ",
|
|
184
|
-
|
|
183
|
+
String(attributes[property] ?? "")
|
|
185
184
|
] }, i))
|
|
186
185
|
}
|
|
187
186
|
);
|
|
188
187
|
}
|
|
189
188
|
}
|
|
190
189
|
|
|
191
|
-
// src/components/abstract-choices/abstract-choices.
|
|
192
|
-
var import_react5 = __toESM(require("react"));
|
|
190
|
+
// src/components/abstract-choices/abstract-choices.tsx
|
|
193
191
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
194
192
|
function AbstractChoices() {
|
|
195
193
|
const { clickTarget, allClickable, undoStep, currentMoveTargets } = useGame();
|
|
196
|
-
const abstractChoices = [...allClickable].filter(
|
|
194
|
+
const abstractChoices = [...allClickable].filter(
|
|
195
|
+
(c) => c.abstract
|
|
196
|
+
);
|
|
197
197
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { position: "relative" }, children: [
|
|
198
198
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
199
199
|
"button",
|
|
200
200
|
{
|
|
201
201
|
style: { visibility: "hidden" },
|
|
202
202
|
className: "button button--style-b button--x-small abstract-choices__choice",
|
|
203
|
+
type: "button",
|
|
203
204
|
children: "Spacer"
|
|
204
205
|
}
|
|
205
206
|
),
|
|
@@ -215,6 +216,7 @@ function AbstractChoices() {
|
|
|
215
216
|
!!currentMoveTargets.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
216
217
|
"button",
|
|
217
218
|
{
|
|
219
|
+
type: "button",
|
|
218
220
|
className: "button button--style-c button--x-small abstract-choices__choice abstract-choices__choice--undo",
|
|
219
221
|
onClick: undoStep,
|
|
220
222
|
children: "Undo"
|
|
@@ -223,9 +225,10 @@ function AbstractChoices() {
|
|
|
223
225
|
abstractChoices.map((choice, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
224
226
|
"button",
|
|
225
227
|
{
|
|
228
|
+
type: "button",
|
|
226
229
|
className: "button button--style-b button--x-small abstract-choices__choice",
|
|
227
230
|
onClick: () => clickTarget(choice),
|
|
228
|
-
children: choice.value
|
|
231
|
+
children: String(choice.value ?? "")
|
|
229
232
|
},
|
|
230
233
|
i
|
|
231
234
|
))
|
|
@@ -235,28 +238,36 @@ function AbstractChoices() {
|
|
|
235
238
|
] });
|
|
236
239
|
}
|
|
237
240
|
|
|
238
|
-
// src/components/game-status/game-status.
|
|
239
|
-
var import_react6 = __toESM(require("react"));
|
|
241
|
+
// src/components/game-status/game-status.tsx
|
|
240
242
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
241
|
-
function GameStatus({
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
243
|
+
function GameStatus({
|
|
244
|
+
gameConnection
|
|
245
|
+
}) {
|
|
246
|
+
const state = gameConnection.state;
|
|
247
|
+
if (!state) return null;
|
|
248
|
+
const players = gameConnection.client?.matchData;
|
|
249
|
+
const winner = state.ctx.gameover?.winner;
|
|
250
|
+
const draw = state.ctx.gameover?.draw;
|
|
245
251
|
let winnerString = "";
|
|
246
252
|
if (draw) {
|
|
247
253
|
winnerString = "Draw!";
|
|
248
|
-
} else if (players && winner) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
} else if (players != null && winner != null && winner !== "") {
|
|
255
|
+
const key = String(winner);
|
|
256
|
+
winnerString = `${players[key]?.name ?? ""} Wins!`;
|
|
257
|
+
} else if (winner != null && winner !== "") {
|
|
258
|
+
winnerString = `Player ${String(winner)} Wins!`;
|
|
252
259
|
}
|
|
253
|
-
return
|
|
260
|
+
return state.ctx.gameover && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "game-status", children: winnerString });
|
|
254
261
|
}
|
|
255
262
|
|
|
256
|
-
// src/components/game/game.
|
|
263
|
+
// src/components/game/game.tsx
|
|
257
264
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
258
|
-
function Game({
|
|
259
|
-
|
|
265
|
+
function Game({
|
|
266
|
+
gameConnection,
|
|
267
|
+
loading,
|
|
268
|
+
isSpectator
|
|
269
|
+
}) {
|
|
270
|
+
const G = gameConnection && "state" in gameConnection ? gameConnection.state?.G : void 0;
|
|
260
271
|
return G ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
261
272
|
GameProvider,
|
|
262
273
|
{
|
|
@@ -300,10 +311,10 @@ function Game({ gameConnection, loading, isSpectator }) {
|
|
|
300
311
|
) : loading;
|
|
301
312
|
}
|
|
302
313
|
|
|
303
|
-
// src/use-gameserver-connection.
|
|
304
|
-
var
|
|
314
|
+
// src/use-gameserver-connection.ts
|
|
315
|
+
var import_react2 = require("react");
|
|
305
316
|
var import_board_game_engine = require("board-game-engine");
|
|
306
|
-
|
|
317
|
+
function useGameserverConnection({
|
|
307
318
|
server,
|
|
308
319
|
multiplayer,
|
|
309
320
|
matchID,
|
|
@@ -315,10 +326,10 @@ var useGameserverConnection = ({
|
|
|
315
326
|
numPlayers,
|
|
316
327
|
debug,
|
|
317
328
|
enabled = true
|
|
318
|
-
})
|
|
319
|
-
const [
|
|
320
|
-
const [connection, setConnection] = (0,
|
|
321
|
-
(0,
|
|
329
|
+
}) {
|
|
330
|
+
const [_tick, forceUpdate] = (0, import_react2.useReducer)((x) => x + 1, 0);
|
|
331
|
+
const [connection, setConnection] = (0, import_react2.useState)(null);
|
|
332
|
+
(0, import_react2.useEffect)(() => {
|
|
322
333
|
if (!gameRules && !boardgameIOGame || credentials && !(matchID && enabled && server)) {
|
|
323
334
|
return;
|
|
324
335
|
}
|
|
@@ -341,7 +352,7 @@ var useGameserverConnection = ({
|
|
|
341
352
|
newConnection.connect();
|
|
342
353
|
setConnection(newConnection);
|
|
343
354
|
return () => {
|
|
344
|
-
|
|
355
|
+
newConnection.client?.stop();
|
|
345
356
|
setConnection(null);
|
|
346
357
|
};
|
|
347
358
|
}, [
|
|
@@ -357,9 +368,8 @@ var useGameserverConnection = ({
|
|
|
357
368
|
if (connection) {
|
|
358
369
|
return Object.assign(
|
|
359
370
|
connection,
|
|
360
|
-
connection
|
|
371
|
+
connection.getState?.()
|
|
361
372
|
);
|
|
362
|
-
} else {
|
|
363
|
-
return {};
|
|
364
373
|
}
|
|
365
|
-
};
|
|
374
|
+
return {};
|
|
375
|
+
}
|