@ankhorage/zora 25.11.0 → 26.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 (106) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -4
  3. package/dist/capabilities/index.js +65 -41
  4. package/dist/capabilities/index.js.map +3 -3
  5. package/dist/features/chess/adapters/inbound/ChessBoard.d.ts +1 -1
  6. package/dist/features/chess/adapters/inbound/ChessBoard.d.ts.map +1 -1
  7. package/dist/features/chess/adapters/inbound/ChessBoard.js +130 -83
  8. package/dist/features/chess/adapters/inbound/ChessBoard.js.map +1 -1
  9. package/dist/features/chess/meta.d.ts +97 -72
  10. package/dist/features/chess/meta.d.ts.map +1 -1
  11. package/dist/features/chess/meta.js +64 -40
  12. package/dist/features/chess/meta.js.map +1 -1
  13. package/dist/features/chess/public.d.ts +1 -7
  14. package/dist/features/chess/public.d.ts.map +1 -1
  15. package/dist/features/chess/public.js +0 -6
  16. package/dist/features/chess/public.js.map +1 -1
  17. package/dist/features/chess/utils/createChessBoardCellPressHandler.d.ts +6 -0
  18. package/dist/features/chess/utils/createChessBoardCellPressHandler.d.ts.map +1 -0
  19. package/dist/features/chess/utils/createChessBoardCellPressHandler.js +17 -0
  20. package/dist/features/chess/utils/createChessBoardCellPressHandler.js.map +1 -0
  21. package/dist/features/chess/utils/createChessBoardCells.d.ts +12 -0
  22. package/dist/features/chess/utils/createChessBoardCells.d.ts.map +1 -0
  23. package/dist/features/chess/utils/createChessBoardCells.js +28 -0
  24. package/dist/features/chess/utils/createChessBoardCells.js.map +1 -0
  25. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.d.ts +2 -0
  26. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.d.ts.map +1 -1
  27. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js +68 -4
  28. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js.map +1 -1
  29. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.d.ts +3 -1
  30. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.d.ts.map +1 -1
  31. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.js +41 -8
  32. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.js.map +1 -1
  33. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.d.ts.map +1 -1
  34. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.js +6 -2
  35. package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.js.map +1 -1
  36. package/dist/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.d.ts +2 -2
  37. package/dist/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.d.ts.map +1 -1
  38. package/dist/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.js +3 -3
  39. package/dist/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.js.map +1 -1
  40. package/dist/readerFree.d.ts +2 -2
  41. package/dist/readerFree.d.ts.map +1 -1
  42. package/dist/readerFree.js +1 -1
  43. package/dist/readerFree.js.map +1 -1
  44. package/dist/types/authoring.d.ts +2 -1
  45. package/dist/types/authoring.d.ts.map +1 -1
  46. package/dist/types/authoring.js.map +1 -1
  47. package/dist/types/chess.d.ts +27 -20
  48. package/dist/types/chess.d.ts.map +1 -1
  49. package/dist/types/chess.js.map +1 -1
  50. package/package.json +31 -92
  51. package/src/features/chess/adapters/inbound/ChessBoard.tsx +263 -112
  52. package/src/features/chess/meta.ts +64 -40
  53. package/src/features/chess/public.test.tsx +77 -228
  54. package/src/features/chess/public.ts +2 -9
  55. package/src/features/chess/utils/createChessBoardCellPressHandler.ts +30 -0
  56. package/src/features/chess/utils/createChessBoardCells.ts +54 -0
  57. package/src/features/chessTabletopOwnership.test.ts +3 -7
  58. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphController.test.ts +145 -1
  59. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts +86 -5
  60. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.test.ts +163 -6
  61. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.ts +57 -8
  62. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.test.ts +42 -0
  63. package/src/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.ts +7 -1
  64. package/src/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.test.ts +7 -3
  65. package/src/features/graph-view/adapters/inbound/web-artifact/shouldFitGraphAfterLayout.ts +7 -3
  66. package/src/features/registry/componentMeta.test.ts +12 -1
  67. package/src/readerFree.ts +3 -15
  68. package/src/types/authoring.ts +2 -1
  69. package/src/types/chess.ts +28 -21
  70. package/web-dist/chunks/Accordion-91sjb3zh.js +453 -0
  71. package/web-dist/components/chess-board/ChessBoard.js +2 -1
  72. package/web-dist/components/graph-view/GraphView.js +107 -17
  73. package/web-dist/components/opening-book/OpeningBook.js +2 -1
  74. package/web-dist/manifest.json +8 -4
  75. package/dist/features/chess/utils/chessEngine.d.ts +0 -8
  76. package/dist/features/chess/utils/chessEngine.d.ts.map +0 -1
  77. package/dist/features/chess/utils/chessEngine.js +0 -71
  78. package/dist/features/chess/utils/chessEngine.js.map +0 -1
  79. package/dist/features/chess/utils/chessSquares.d.ts +0 -4
  80. package/dist/features/chess/utils/chessSquares.d.ts.map +0 -1
  81. package/dist/features/chess/utils/chessSquares.js +0 -3
  82. package/dist/features/chess/utils/chessSquares.js.map +0 -1
  83. package/dist/features/chess/utils/createBoardSquares.d.ts +0 -4
  84. package/dist/features/chess/utils/createBoardSquares.d.ts.map +0 -1
  85. package/dist/features/chess/utils/createBoardSquares.js +0 -9
  86. package/dist/features/chess/utils/createBoardSquares.js.map +0 -1
  87. package/dist/features/chess/utils/getSquareFile.d.ts +0 -4
  88. package/dist/features/chess/utils/getSquareFile.d.ts.map +0 -1
  89. package/dist/features/chess/utils/getSquareFile.js +0 -5
  90. package/dist/features/chess/utils/getSquareFile.js.map +0 -1
  91. package/dist/features/chess/utils/getSquareRank.d.ts +0 -4
  92. package/dist/features/chess/utils/getSquareRank.d.ts.map +0 -1
  93. package/dist/features/chess/utils/getSquareRank.js +0 -5
  94. package/dist/features/chess/utils/getSquareRank.js.map +0 -1
  95. package/dist/features/chess/utils/isLightSquare.d.ts +0 -4
  96. package/dist/features/chess/utils/isLightSquare.d.ts.map +0 -1
  97. package/dist/features/chess/utils/isLightSquare.js +0 -6
  98. package/dist/features/chess/utils/isLightSquare.js.map +0 -1
  99. package/src/features/chess/utils/chessEngine.test.ts +0 -35
  100. package/src/features/chess/utils/chessEngine.ts +0 -83
  101. package/src/features/chess/utils/chessSquares.ts +0 -6
  102. package/src/features/chess/utils/createBoardSquares.ts +0 -13
  103. package/src/features/chess/utils/getSquareFile.ts +0 -6
  104. package/src/features/chess/utils/getSquareRank.ts +0 -6
  105. package/src/features/chess/utils/isLightSquare.ts +0 -8
  106. package/web-dist/chunks/Accordion-xcm1qc39.js +0 -3813
@@ -2,17 +2,18 @@ import React from 'react';
2
2
  import { Pressable, StyleSheet, Text, View } from 'react-native';
3
3
 
4
4
  import type {
5
+ ChessBoardCell,
6
+ ChessBoardDimensions,
5
7
  ChessBoardProps,
6
8
  ChessMoveAttempt,
7
9
  ChessPieceState,
8
- ChessSquareId,
9
10
  } from '../../../../types/chess';
11
+ import { GridView } from '../../../grid-view/public';
10
12
  import { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope';
11
13
  import { useZoraTheme } from '../../../theme/composition/useZoraTheme';
12
- import { getLegalTargets, readChessPieces, tryMove } from '../../utils/chessEngine';
13
- import { createBoardSquares } from '../../utils/createBoardSquares';
14
+ import { createChessBoardCellPressHandler } from '../../utils/createChessBoardCellPressHandler';
15
+ import { createChessBoardCells } from '../../utils/createChessBoardCells';
14
16
  import { createChessBoardColorScheme } from '../../utils/createChessBoardColorScheme';
15
- import { isLightSquare } from '../../utils/isLightSquare';
16
17
 
17
18
  const symbols = new Map<string, string>([
18
19
  ['white:p', '♙'],
@@ -28,138 +29,293 @@ const symbols = new Map<string, string>([
28
29
  ['black:q', '♛'],
29
30
  ['black:k', '♚'],
30
31
  ]);
32
+ const minimumDimension = 2;
33
+ const maximumDimension = 24;
31
34
 
32
- /*** Renders caller-owned or FEN-backed chess state with optional legal-move validation. */
35
+ /*** Renders a configurable caller-owned chess presentation through the shared GridView surface. */
33
36
  export const ChessBoard = withZoraThemeScope(ChessBoardInner);
34
37
 
35
- /*** Renders the current presentation state and delegates FEN validation to the core chess engine. */
38
+ /*** Renders board state and emits caller-owned cell and move intents without applying chess rules. */
36
39
  function ChessBoardInner({
37
- fen,
40
+ columns = 8,
41
+ rows = 8,
38
42
  pieces,
39
43
  orientation = 'white',
40
- selectedSquare = null,
41
- legalTargets,
44
+ selectedCell = null,
45
+ legalTargets = [],
42
46
  lastMove = null,
43
47
  disabled = false,
44
48
  showCoordinates = false,
45
- validateMoves = true,
49
+ resizable = false,
46
50
  colorScheme,
47
- onSquarePress,
51
+ onCellPress,
48
52
  onMoveAttempt,
49
- onLegalMove,
50
- onInvalidMove,
53
+ onDimensionsChange,
51
54
  renderPiece,
52
55
  interactionPolicy,
53
56
  testID,
54
57
  }: ChessBoardProps) {
55
58
  const { theme } = useZoraTheme();
56
59
  const colors = createChessBoardColorScheme(theme, colorScheme);
57
- const squares = createBoardSquares(orientation);
58
- const piecesBySquare =
59
- pieces === undefined
60
- ? fen === undefined
61
- ? new Map<ChessSquareId, ChessPieceState>()
62
- : new Map(readChessPieces(fen))
63
- : new Map<ChessSquareId, ChessPieceState>(pieces.map((piece) => [piece.square, piece]));
64
- const resolvedLegalTargets =
65
- legalTargets ??
66
- (fen !== undefined && selectedSquare !== null ? getLegalTargets(fen, selectedSquare) : []);
67
- const legalTargetSet = new Set(resolvedLegalTargets);
68
- const pressSquare = (square: ChessSquareId) => {
69
- if (disabled || interactionPolicy === 'passive') return;
70
- onSquarePress?.(square);
71
- if (selectedSquare === null || selectedSquare === square) return;
72
-
73
- const attempt: ChessMoveAttempt = { from: selectedSquare, to: square };
74
- onMoveAttempt?.(attempt);
75
- if (!validateMoves || fen === undefined) return;
60
+ const dimensions = React.useMemo(() => ({ columns, rows }), [columns, rows]);
61
+ const [width, setWidth] = React.useState(0);
62
+ const cells = React.useMemo(
63
+ () => createChessBoardCells(columns, rows, orientation),
64
+ [columns, orientation, rows],
65
+ );
66
+ const piecesByCell = React.useMemo(
67
+ () => new Map((pieces ?? []).map((piece) => [getCellKey(piece.cell), piece])),
68
+ [pieces],
69
+ );
70
+ const cellById = React.useMemo(() => new Map(cells.map((cell) => [cell.id, cell])), [cells]);
71
+ const legalTargetSet = React.useMemo(() => new Set(legalTargets.map(getCellKey)), [legalTargets]);
72
+ const pressCell = createChessBoardCellPressHandler({
73
+ disabled,
74
+ interactionPolicy,
75
+ onCellPress,
76
+ onMoveAttempt,
77
+ selectedCell,
78
+ });
79
+ const canResize =
80
+ resizable && onDimensionsChange !== undefined && !disabled && interactionPolicy !== 'passive';
81
+ const height = width * (dimensions.rows / dimensions.columns);
82
+ const cellSize = width / dimensions.columns;
76
83
 
77
- const result = tryMove(fen, attempt);
78
- if (result) onLegalMove?.(result);
79
- else onInvalidMove?.(attempt);
80
- };
81
84
  return (
82
85
  <View
83
86
  accessibilityLabel="Chess board"
84
- style={[styles.board, { borderColor: colors.border, opacity: disabled ? 0.56 : 1 }]}
87
+ onLayout={(event) => setWidth(event.nativeEvent.layout.width)}
88
+ style={[
89
+ styles.board,
90
+ {
91
+ aspectRatio: dimensions.columns / dimensions.rows,
92
+ borderColor: colors.border,
93
+ opacity: disabled ? 0.56 : 1,
94
+ },
95
+ ]}
85
96
  testID={testID}
86
97
  >
87
- {squares.map((square) => {
88
- const piece = piecesBySquare.get(square);
89
- const light = isLightSquare(square);
90
- const isTarget = legalTargetSet.has(square);
91
- const backgroundColor =
92
- selectedSquare === square
93
- ? colors.selectedSquare
94
- : isTarget
95
- ? colors.legalTarget
96
- : lastMove?.from === square
97
- ? colors.lastMoveFrom
98
- : lastMove?.to === square
99
- ? colors.lastMoveTo
100
- : light
101
- ? colors.lightSquare
102
- : colors.darkSquare;
103
- const pieceContent =
104
- piece === undefined
105
- ? null
106
- : renderPiece
107
- ? renderPiece({
98
+ {width > 0 ? (
99
+ <GridView
100
+ contentHeight={dimensions.rows}
101
+ contentWidth={dimensions.columns}
102
+ defaultViewport={{ pixelsPerUnitX: cellSize, pixelsPerUnitY: cellSize }}
103
+ height={height}
104
+ interactionPolicy="passive"
105
+ items={cells}
106
+ overscanPixels={0}
107
+ renderItem={(item) => {
108
+ const cell = cellById.get(item.id);
109
+ return cell === undefined
110
+ ? null
111
+ : renderChessBoardCell({
112
+ cell,
113
+ cellSize,
114
+ colors,
115
+ disabled: disabled || interactionPolicy === 'passive',
116
+ lastMove,
117
+ legalTargetSet,
118
+ onPress: pressCell,
119
+ piece: piecesByCell.get(item.id),
120
+ renderPiece,
121
+ selectedCell,
122
+ showCoordinates,
123
+ testID,
124
+ });
125
+ }}
126
+ width={width}
127
+ />
128
+ ) : null}
129
+ {canResize ? renderChessBoardResizeActions(dimensions, onDimensionsChange, testID) : null}
130
+ </View>
131
+ );
132
+ }
133
+
134
+ /*** Render one accessible, caller-controlled rectangular board cell. */
135
+ function renderChessBoardCell({
136
+ cell,
137
+ cellSize,
138
+ colors,
139
+ disabled,
140
+ lastMove,
141
+ legalTargetSet,
142
+ onPress,
143
+ piece,
144
+ renderPiece,
145
+ selectedCell,
146
+ showCoordinates,
147
+ testID,
148
+ }: {
149
+ readonly cell: ChessBoardCell & { readonly id: string };
150
+ readonly cellSize: number;
151
+ readonly colors: ReturnType<typeof createChessBoardColorScheme>;
152
+ readonly disabled: boolean;
153
+ readonly lastMove: ChessMoveAttempt | null;
154
+ readonly legalTargetSet: ReadonlySet<string>;
155
+ readonly onPress: (cell: ChessBoardCell) => void;
156
+ readonly piece: ChessPieceState | undefined;
157
+ readonly renderPiece: ChessBoardProps['renderPiece'];
158
+ readonly selectedCell: ChessBoardCell | null;
159
+ readonly showCoordinates: boolean;
160
+ readonly testID: string | undefined;
161
+ }) {
162
+ const light = (cell.column + cell.row) % 2 === 1;
163
+ const key = getCellKey(cell);
164
+ const backgroundColor = cellsEqual(selectedCell, cell)
165
+ ? colors.selectedSquare
166
+ : legalTargetSet.has(key)
167
+ ? colors.legalTarget
168
+ : cellsEqual(lastMove?.from, cell)
169
+ ? colors.lastMoveFrom
170
+ : cellsEqual(lastMove?.to, cell)
171
+ ? colors.lastMoveTo
172
+ : light
173
+ ? colors.lightSquare
174
+ : colors.darkSquare;
175
+ const pieceContent =
176
+ piece === undefined
177
+ ? null
178
+ : renderPiece
179
+ ? renderPiece({
180
+ cell,
181
+ color: piece.color === 'white' ? colors.lightPiece : colors.darkPiece,
182
+ piece: piece.piece,
183
+ size: cellSize,
184
+ })
185
+ : (symbols.get(`${piece.color}:${piece.piece.toLowerCase()}`) ?? piece.piece);
186
+ const coordinate = formatCellCoordinate(cell);
187
+
188
+ return (
189
+ <Pressable
190
+ accessibilityLabel={coordinate}
191
+ accessibilityRole="button"
192
+ accessibilityState={{ disabled }}
193
+ disabled={disabled}
194
+ key={key}
195
+ onPress={() => onPress(cell)}
196
+ style={[styles.square, { backgroundColor }]}
197
+ testID={testID === undefined ? undefined : `${testID}-cell-${coordinate}`}
198
+ >
199
+ {showCoordinates ? (
200
+ <Text
201
+ style={[
202
+ styles.coordinate,
203
+ { color: light ? colors.lightSquareText : colors.darkSquareText },
204
+ ]}
205
+ >
206
+ {coordinate}
207
+ </Text>
208
+ ) : null}
209
+ {piece === undefined ? null : (
210
+ <View pointerEvents="none" style={styles.pieceContainer}>
211
+ {renderPiece ? (
212
+ pieceContent
213
+ ) : (
214
+ <Text
215
+ style={[
216
+ styles.piece,
217
+ {
108
218
  color: piece.color === 'white' ? colors.lightPiece : colors.darkPiece,
109
- piece: piece.piece,
110
- square,
111
- })
112
- : (symbols.get(`${piece.color}:${piece.piece.toLowerCase()}`) ?? piece.piece);
113
- return (
114
- <Pressable
115
- accessibilityLabel={square}
116
- accessibilityRole="button"
117
- disabled={disabled || interactionPolicy === 'passive'}
118
- key={square}
119
- onPress={() => pressSquare(square)}
120
- style={[styles.square, { backgroundColor }]}
121
- testID={testID ? `${testID}-square-${square}` : undefined}
122
- >
123
- {showCoordinates ? (
124
- <Text
125
- style={[
126
- styles.coordinate,
127
- { color: light ? colors.lightSquareText : colors.darkSquareText },
128
- ]}
129
- >
130
- {square}
131
- </Text>
132
- ) : null}
133
- {piece === undefined ? null : (
134
- <View pointerEvents="none" style={styles.pieceContainer}>
135
- {renderPiece ? (
136
- pieceContent
137
- ) : (
138
- <Text
139
- style={[
140
- styles.piece,
141
- { color: piece.color === 'white' ? colors.lightPiece : colors.darkPiece },
142
- ]}
143
- >
144
- {pieceContent}
145
- </Text>
146
- )}
147
- </View>
148
- )}
149
- </Pressable>
150
- );
151
- })}
219
+ fontSize: cellSize * 0.58,
220
+ lineHeight: cellSize * 0.7,
221
+ },
222
+ ]}
223
+ >
224
+ {pieceContent}
225
+ </Text>
226
+ )}
227
+ </View>
228
+ )}
229
+ </Pressable>
230
+ );
231
+ }
232
+
233
+ /*** Render bounded editor actions only when the caller explicitly enables dimension editing. */
234
+ function renderChessBoardResizeActions(
235
+ dimensions: ChessBoardDimensions,
236
+ onDimensionsChange: (dimensions: ChessBoardDimensions) => void,
237
+ testID: string | undefined,
238
+ ) {
239
+ return (
240
+ <View accessibilityLabel="Chess board dimensions" style={styles.resizeActions}>
241
+ {renderResizeAction(
242
+ 'Add row',
243
+ { ...dimensions, rows: dimensions.rows + 1 },
244
+ onDimensionsChange,
245
+ dimensions.rows < maximumDimension,
246
+ testID,
247
+ )}
248
+ {renderResizeAction(
249
+ 'Remove row',
250
+ { ...dimensions, rows: dimensions.rows - 1 },
251
+ onDimensionsChange,
252
+ dimensions.rows > minimumDimension,
253
+ testID,
254
+ )}
255
+ {renderResizeAction(
256
+ 'Add column',
257
+ { ...dimensions, columns: dimensions.columns + 1 },
258
+ onDimensionsChange,
259
+ dimensions.columns < maximumDimension,
260
+ testID,
261
+ )}
262
+ {renderResizeAction(
263
+ 'Remove column',
264
+ { ...dimensions, columns: dimensions.columns - 1 },
265
+ onDimensionsChange,
266
+ dimensions.columns > minimumDimension,
267
+ testID,
268
+ )}
152
269
  </View>
153
270
  );
154
271
  }
155
272
 
273
+ /*** Render one caller-controlled dimension-change action. */
274
+ function renderResizeAction(
275
+ label: string,
276
+ dimensions: ChessBoardDimensions,
277
+ onDimensionsChange: (dimensions: ChessBoardDimensions) => void,
278
+ enabled: boolean,
279
+ testID: string | undefined,
280
+ ) {
281
+ return (
282
+ <Pressable
283
+ accessibilityLabel={label}
284
+ accessibilityRole="button"
285
+ disabled={!enabled}
286
+ onPress={() => onDimensionsChange(dimensions)}
287
+ testID={
288
+ testID === undefined ? undefined : `${testID}-${label.toLowerCase().replace(' ', '-')}`
289
+ }
290
+ >
291
+ <Text>{label}</Text>
292
+ </Pressable>
293
+ );
294
+ }
295
+
296
+ /*** Compares optional cell coordinates without attaching chess-rule meaning to them. */
297
+ function cellsEqual(
298
+ left: ChessBoardCell | undefined | null,
299
+ right: ChessBoardCell | undefined | null,
300
+ ): boolean {
301
+ if (!left || !right) return false;
302
+ return left.column === right.column && left.row === right.row;
303
+ }
304
+
305
+ /*** Format a stable display label for a bounded board cell. */
306
+ function formatCellCoordinate(cell: ChessBoardCell): string {
307
+ return `${String.fromCharCode(96 + cell.column)}${cell.row}`;
308
+ }
309
+
310
+ /*** Create a stable generic identity from caller-owned row and column values. */
311
+ function getCellKey(cell: ChessBoardCell): string {
312
+ return `${cell.column}:${cell.row}`;
313
+ }
314
+
156
315
  const styles = StyleSheet.create({
157
316
  board: {
158
- aspectRatio: 1,
159
317
  borderRadius: 12,
160
318
  borderWidth: StyleSheet.hairlineWidth,
161
- flexDirection: 'row',
162
- flexWrap: 'wrap',
163
319
  overflow: 'hidden',
164
320
  width: '100%',
165
321
  },
@@ -172,7 +328,7 @@ const styles = StyleSheet.create({
172
328
  top: 2,
173
329
  zIndex: 2,
174
330
  },
175
- piece: { fontSize: 32, fontWeight: '600', lineHeight: 38 },
331
+ piece: { fontWeight: '600' },
176
332
  pieceContainer: {
177
333
  alignItems: 'center',
178
334
  bottom: 0,
@@ -182,11 +338,6 @@ const styles = StyleSheet.create({
182
338
  right: 0,
183
339
  top: 0,
184
340
  },
185
- square: {
186
- alignItems: 'center',
187
- aspectRatio: 1,
188
- width: '12.5%',
189
- justifyContent: 'center',
190
- position: 'relative',
191
- },
341
+ resizeActions: { flexDirection: 'row', gap: 8, padding: 8 },
342
+ square: { alignItems: 'center', justifyContent: 'center', position: 'relative' },
192
343
  });
@@ -9,42 +9,31 @@ export const chessBoardMeta = {
9
9
  allowedChildren: [],
10
10
  blueprint: {
11
11
  label: 'Chess board',
12
- defaultProps: { pieces: [], orientation: 'white', showCoordinates: true, validateMoves: true },
12
+ defaultProps: {
13
+ columns: 8,
14
+ pieces: [],
15
+ orientation: 'white',
16
+ rows: 8,
17
+ showCoordinates: true,
18
+ },
13
19
  },
14
20
  events: {
15
- squarePress: {
16
- label: 'Square press',
17
- eventType: 'chess.squarePress',
18
- payloadFields: [{ path: 'square', type: 'string', label: 'Square' }],
21
+ cellPress: {
22
+ label: 'Cell press',
23
+ eventType: 'chess.cellPress',
24
+ payloadFields: [
25
+ { path: 'column', type: 'number', label: 'Column' },
26
+ { path: 'row', type: 'number', label: 'Row' },
27
+ ],
19
28
  },
20
29
  moveAttempt: {
21
30
  label: 'Move attempt',
22
31
  eventType: 'chess.moveAttempt',
23
32
  payloadFields: [
24
- { path: 'from', type: 'string', label: 'From' },
25
- { path: 'to', type: 'string', label: 'To' },
26
- { path: 'promotion', type: 'string', label: 'Promotion' },
27
- ],
28
- },
29
- legalMove: {
30
- label: 'Legal move',
31
- eventType: 'chess.legalMove',
32
- payloadFields: [
33
- { path: 'from', type: 'string', label: 'From' },
34
- { path: 'to', type: 'string', label: 'To' },
35
- { path: 'fen', type: 'string', label: 'Position' },
36
- { path: 'san', type: 'string', label: 'SAN' },
37
- { path: 'lan', type: 'string', label: 'LAN' },
38
- { path: 'promotion', type: 'string', label: 'Promotion' },
39
- ],
40
- },
41
- invalidMove: {
42
- label: 'Invalid move',
43
- eventType: 'chess.invalidMove',
44
- payloadFields: [
45
- { path: 'from', type: 'string', label: 'From' },
46
- { path: 'to', type: 'string', label: 'To' },
47
- { path: 'promotion', type: 'string', label: 'Promotion' },
33
+ { path: 'from.column', type: 'number', label: 'From column' },
34
+ { path: 'from.row', type: 'number', label: 'From row' },
35
+ { path: 'to.column', type: 'number', label: 'To column' },
36
+ { path: 'to.row', type: 'number', label: 'To row' },
48
37
  ],
49
38
  },
50
39
  },
@@ -56,19 +45,46 @@ export const chessBoardMeta = {
56
45
  acceptsFallback: true,
57
46
  acceptsTransforms: true,
58
47
  },
48
+ selectedCell: {
49
+ label: 'Selected cell',
50
+ value: {
51
+ type: 'object',
52
+ fields: [
53
+ { path: 'column', type: 'number', label: 'Column' },
54
+ { path: 'row', type: 'number', label: 'Row' },
55
+ ],
56
+ },
57
+ acceptsFallback: true,
58
+ acceptsTransforms: true,
59
+ },
59
60
  legalTargets: {
60
61
  label: 'Legal targets',
61
- value: { type: 'array', itemType: 'string' },
62
+ value: {
63
+ type: 'array',
64
+ itemType: 'object',
65
+ fields: [
66
+ { path: '[].column', type: 'number', label: 'Column' },
67
+ { path: '[].row', type: 'number', label: 'Row' },
68
+ ],
69
+ },
62
70
  acceptsFallback: true,
63
71
  acceptsTransforms: true,
64
72
  },
65
73
  },
66
74
  },
67
75
  props: {
68
- fen: {
69
- type: 'string',
70
- category: 'Position',
71
- label: 'FEN',
76
+ columns: {
77
+ type: 'number',
78
+ category: 'Board',
79
+ label: 'Columns',
80
+ default: 8,
81
+ authoring: { authority: 'instance' },
82
+ },
83
+ rows: {
84
+ type: 'number',
85
+ category: 'Board',
86
+ label: 'Rows',
87
+ default: 8,
72
88
  authoring: { authority: 'instance' },
73
89
  },
74
90
  pieces: {
@@ -86,10 +102,14 @@ export const chessBoardMeta = {
86
102
  default: 'white',
87
103
  authoring: { authority: 'instance' },
88
104
  },
89
- selectedSquare: {
90
- type: 'string',
105
+ selectedCell: {
106
+ type: 'object',
91
107
  category: 'Board',
92
- label: 'Selected square',
108
+ label: 'Selected cell',
109
+ itemSchema: [
110
+ { key: 'column', schema: { type: 'number', category: 'Cell' } },
111
+ { key: 'row', schema: { type: 'number', category: 'Cell' } },
112
+ ],
93
113
  authoring: { authority: 'instance' },
94
114
  },
95
115
  legalTargets: {
@@ -97,6 +117,10 @@ export const chessBoardMeta = {
97
117
  category: 'Board',
98
118
  label: 'Legal targets',
99
119
  default: [],
120
+ itemSchema: [
121
+ { key: 'column', schema: { type: 'number', category: 'Cell' } },
122
+ { key: 'row', schema: { type: 'number', category: 'Cell' } },
123
+ ],
100
124
  authoring: { authority: 'instance' },
101
125
  },
102
126
  showCoordinates: {
@@ -106,11 +130,11 @@ export const chessBoardMeta = {
106
130
  default: false,
107
131
  authoring: { authority: 'instance' },
108
132
  },
109
- validateMoves: {
133
+ resizable: {
110
134
  type: 'boolean',
111
135
  category: 'Behavior',
112
- label: 'Validate moves',
113
- default: true,
136
+ label: 'Resizable',
137
+ default: false,
114
138
  authoring: { authority: 'instance' },
115
139
  },
116
140
  disabled: {