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.
Files changed (56) hide show
  1. package/.storybook/webpack.config.js +1 -1
  2. package/babel.config.js +2 -1
  3. package/dist/board-game-engine-react.cjs +100 -90
  4. package/dist/board-game-engine-react.mjs +103 -78
  5. package/dist/components/abstract-choices/abstract-choices.d.ts +2 -0
  6. package/dist/components/abstract-choices/abstract-choices.d.ts.map +1 -0
  7. package/dist/components/board/grid.d.ts +5 -0
  8. package/dist/components/board/grid.d.ts.map +1 -0
  9. package/dist/components/entity/entity.d.ts +5 -0
  10. package/dist/components/entity/entity.d.ts.map +1 -0
  11. package/dist/components/game/game.d.ts +8 -0
  12. package/dist/components/game/game.d.ts.map +1 -0
  13. package/dist/components/game-status/game-status.d.ts +5 -0
  14. package/dist/components/game-status/game-status.d.ts.map +1 -0
  15. package/dist/components/space/space.d.ts +5 -0
  16. package/dist/components/space/space.d.ts.map +1 -0
  17. package/dist/contexts/game-context.d.ts +15 -0
  18. package/dist/contexts/game-context.d.ts.map +1 -0
  19. package/dist/index.d.ts +8 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/types.d.ts +24 -0
  22. package/dist/types.d.ts.map +1 -0
  23. package/dist/use-gameserver-connection.d.ts +8 -0
  24. package/dist/use-gameserver-connection.d.ts.map +1 -0
  25. package/examples/checkers.json +2 -2
  26. package/examples/connect-four.json +1 -1
  27. package/examples/eights.json +15 -15
  28. package/examples/index.html +219 -0
  29. package/package.json +12 -10
  30. package/scripts/build.mjs +3 -3
  31. package/src/components/abstract-choices/{abstract-choices.js → abstract-choices.tsx} +12 -8
  32. package/src/components/board/grid.tsx +29 -0
  33. package/src/components/entity/entity.tsx +40 -0
  34. package/src/components/game/{game.js → game.tsx} +22 -9
  35. package/src/components/game-status/game-status.tsx +30 -0
  36. package/src/components/space/space.tsx +52 -0
  37. package/src/contexts/game-context.tsx +60 -0
  38. package/src/global.d.ts +1 -0
  39. package/src/index.ts +8 -0
  40. package/src/types.ts +25 -0
  41. package/src/use-gameserver-connection.ts +82 -0
  42. package/stories/BoardGameEngineReact.stories.tsx +85 -0
  43. package/test-app/index.html +1 -1
  44. package/test-app/{main.jsx → main.tsx} +13 -11
  45. package/tsconfig.build.json +14 -0
  46. package/tsconfig.json +34 -0
  47. package/board-game-engine-react.test.js +0 -12
  48. package/jest.config.js +0 -13
  49. package/src/components/board/grid.js +0 -28
  50. package/src/components/entity/entity.js +0 -38
  51. package/src/components/game-status/game-status.js +0 -20
  52. package/src/components/space/space.js +0 -68
  53. package/src/contexts/game-context.js +0 -35
  54. package/src/index.js +0 -5
  55. package/src/use-gameserver-connection.js +0 -74
  56. 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.js')
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/**/*.js'], exclude: ['**/*.spec.js', '**/node_modules/**', 'e2e/**'] }]]
12
+ ? [['istanbul', { include: ['src/**/*.ts', 'src/**/*.tsx'], exclude: ['**/*.spec.js', '**/node_modules/**', 'e2e/**'] }]]
12
13
  : [])
13
14
  ]
14
15
  }
@@ -1,8 +1,7 @@
1
- var __create = Object.create;
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.js
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/components/game/game.js
41
- var import_react7 = __toESM(require("react"));
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({ gameConnection, children, isSpectator }) {
42
+ function GameProvider({
43
+ gameConnection,
44
+ children,
45
+ isSpectator
46
+ }) {
54
47
  (0, import_react.useEffect)(() => {
55
- if (gameConnection.state._stateID === 0) {
48
+ if (gameConnection.state?._stateID === 0) {
56
49
  gameConnection.reset();
57
50
  }
58
- }, [gameConnection.state._stateID]);
59
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GameContext.Provider, { value: {
60
- clickTarget: (target) => {
61
- if (!isSpectator) {
62
- gameConnection.doStep(target);
63
- }
64
- },
65
- undoStep: () => {
66
- gameConnection.undoStep();
67
- },
68
- allClickable: gameConnection.optimisticWinner || isSpectator ? /* @__PURE__ */ new Set() : gameConnection.allClickable,
69
- currentMoveTargets: gameConnection.optimisticWinner || isSpectator ? [] : gameConnection.moveBuilder.targets
70
- }, children });
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.js
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(${width}, 1fr)`,
87
- gridTemplateRows: `repeat(${height}, 1fr)`
87
+ gridTemplateColumns: `repeat(${w}, 1fr)`,
88
+ gridTemplateRows: `repeat(${h}, 1fr)`
88
89
  },
89
- children: spaces.map((space, index) => {
90
- return /* @__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
- );
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.js
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?.map((e) => e.entityId).includes(entityId);
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: entities.length }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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: entities[i] })
141
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Entity, { entity: entityList[i] })
143
142
  },
144
143
  i
145
144
  )),
146
- !entities.length && space.attributes.name
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.js
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, isClickable });
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, isClickable });
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
- entity.attributes[property]?.toString()
183
+ String(attributes[property] ?? "")
185
184
  ] }, i))
186
185
  }
187
186
  );
188
187
  }
189
188
  }
190
189
 
191
- // src/components/abstract-choices/abstract-choices.js
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((c) => c.abstract);
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.js
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({ gameConnection }) {
242
- const players = gameConnection.client.matchData;
243
- const winner = gameConnection.state.ctx.gameover?.winner;
244
- const draw = gameConnection.state.ctx.gameover?.draw;
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
- winnerString = `${players[winner].name} Wins!`;
250
- } else if (winner) {
251
- winnerString = `Player ${winner} Wins!`;
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 gameConnection.state.ctx.gameover && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "game-status", children: winnerString });
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.js
263
+ // src/components/game/game.tsx
257
264
  var import_jsx_runtime7 = require("react/jsx-runtime");
258
- function Game({ gameConnection, loading, isSpectator }) {
259
- const G = gameConnection?.state?.G;
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.js
304
- var import_react8 = require("react");
314
+ // src/use-gameserver-connection.ts
315
+ var import_react2 = require("react");
305
316
  var import_board_game_engine = require("board-game-engine");
306
- var useGameserverConnection = ({
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 [_, forceUpdate] = (0, import_react8.useReducer)((x) => x + 1, 0);
320
- const [connection, setConnection] = (0, import_react8.useState)(null);
321
- (0, import_react8.useEffect)(() => {
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
- connection?.client?.stop();
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?.getState?.()
371
+ connection.getState?.()
361
372
  );
362
- } else {
363
- return {};
364
373
  }
365
- };
374
+ return {};
375
+ }