@scriptonita/chess-football-ui 0.1.1 → 0.2.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/dist/index.cjs +351 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +354 -152
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useGameStore, getInitialBoardState } from './chunk-YLNDQ4HG.js';
|
|
2
|
-
import { getValidMoves, getValidPasses, RANK_LABELS, FILE_LABELS, LONG_KEY, squareName, SHORT_KEY } from '@scriptonita/chess-football-engine';
|
|
2
|
+
import { getValidMoves, getValidPasses, isInEnemyArea, RANK_LABELS, FILE_LABELS, LONG_KEY, squareName, SHORT_KEY } from '@scriptonita/chess-football-engine';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
|
-
import { AnimatePresence, motion } from 'framer-motion';
|
|
6
|
-
import { Crown, Zap,
|
|
7
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { MotionConfig, AnimatePresence, motion } from 'framer-motion';
|
|
6
|
+
import { Crown, Zap, Flag, ShieldQuestion, ChessKnight, ChessBishop, ChessRook, ChessQueen, ChessKing, X, Move } from 'lucide-react';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import * as React from 'react';
|
|
9
|
-
import { createContext, useId, useState, useRef, useEffect, useMemo
|
|
9
|
+
import { createContext, useId, useContext, useState, useRef, useEffect, useMemo } from 'react';
|
|
10
10
|
|
|
11
11
|
function cn(...inputs) {
|
|
12
12
|
return twMerge(clsx(inputs));
|
|
@@ -18,14 +18,18 @@ var PIECE_ICON = {
|
|
|
18
18
|
bishop: ChessBishop,
|
|
19
19
|
knight: ChessKnight
|
|
20
20
|
};
|
|
21
|
-
var
|
|
21
|
+
var EMBOSS_FILTER = {
|
|
22
|
+
white: "drop-shadow(0 -0.75px 0 rgba(255,255,255,0.65)) drop-shadow(0 1.25px 1px rgba(0,0,0,0.45))",
|
|
23
|
+
black: "drop-shadow(0 -0.5px 0 rgba(255,255,255,0.3)) drop-shadow(0 1.25px 1.5px rgba(0,0,0,0.8))"
|
|
24
|
+
};
|
|
25
|
+
var PieceIcon = ({ type, side }) => {
|
|
22
26
|
const Icon = PIECE_ICON[type] ?? ShieldQuestion;
|
|
23
27
|
const style = {
|
|
24
28
|
width: "clamp(16px, 7.4cqw, 44px)",
|
|
25
29
|
height: "clamp(16px, 7.4cqw, 44px)",
|
|
26
|
-
filter:
|
|
30
|
+
filter: EMBOSS_FILTER[side]
|
|
27
31
|
};
|
|
28
|
-
return /* @__PURE__ */ jsx(Icon, { style, strokeWidth:
|
|
32
|
+
return /* @__PURE__ */ jsx(Icon, { style, strokeWidth: 2, "aria-hidden": "true" });
|
|
29
33
|
};
|
|
30
34
|
var WHITE_PIECE_STYLE = {
|
|
31
35
|
background: "radial-gradient(circle at 30% 25%, #ffffff 0%, #f4f4f5 55%, #d4d4d8 100%)",
|
|
@@ -66,8 +70,16 @@ function GamePiece({ piece, isSelected, hasBall, onClick }) {
|
|
|
66
70
|
piece.hasMovedThisTurn && "opacity-70"
|
|
67
71
|
),
|
|
68
72
|
children: [
|
|
69
|
-
/* @__PURE__ */ jsx(PieceIcon, { type: piece.type }),
|
|
73
|
+
/* @__PURE__ */ jsx(PieceIcon, { type: piece.type, side: piece.side }),
|
|
70
74
|
hasBall && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full ring-2 ring-orange-400 pointer-events-none" }),
|
|
75
|
+
piece.hasMovedThisTurn && /* @__PURE__ */ jsx(
|
|
76
|
+
"span",
|
|
77
|
+
{
|
|
78
|
+
"aria-hidden": "true",
|
|
79
|
+
className: "absolute top-0.5 right-0.5 w-[14px] h-[14px] rounded-full bg-fg-muted/80 flex items-center justify-center text-[9px] text-bg-primary font-bold leading-none pointer-events-none",
|
|
80
|
+
children: "\u2713"
|
|
81
|
+
}
|
|
82
|
+
),
|
|
71
83
|
isSelected && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full animate-pulse ring-2 ring-yellow-400 ring-offset-2 ring-offset-transparent" })
|
|
72
84
|
]
|
|
73
85
|
}
|
|
@@ -121,20 +133,29 @@ var Football = ({ className }) => {
|
|
|
121
133
|
}
|
|
122
134
|
);
|
|
123
135
|
};
|
|
136
|
+
var fallback = (key) => key;
|
|
137
|
+
var GameI18nContext = createContext(fallback);
|
|
138
|
+
function GameI18nProvider({ t, children }) {
|
|
139
|
+
return /* @__PURE__ */ jsx(GameI18nContext.Provider, { value: t, children: /* @__PURE__ */ jsx(MotionConfig, { reducedMotion: "user", children }) });
|
|
140
|
+
}
|
|
141
|
+
function useGameT() {
|
|
142
|
+
return useContext(GameI18nContext);
|
|
143
|
+
}
|
|
124
144
|
var COLS = 9;
|
|
125
145
|
var ROWS = 12;
|
|
126
146
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
127
|
-
function GameBoard({ userSide, showCoordinates = false, keyboardNav =
|
|
147
|
+
function GameBoard({ userSide, showCoordinates = false, keyboardNav = true }) {
|
|
148
|
+
const t = useGameT();
|
|
128
149
|
const {
|
|
129
150
|
boardState,
|
|
130
151
|
selectedPieceId,
|
|
131
152
|
setSelectedPieceId,
|
|
132
|
-
interactionMode,
|
|
133
153
|
setInteractionMode,
|
|
134
154
|
movePiece,
|
|
135
155
|
passBall
|
|
136
156
|
} = useGameStore();
|
|
137
157
|
const [cursor, setCursor] = useState(null);
|
|
158
|
+
const [disambiguateAt, setDisambiguateAt] = useState(null);
|
|
138
159
|
const prevBallRef = useRef(boardState?.ball);
|
|
139
160
|
const prevBall = prevBallRef.current;
|
|
140
161
|
useEffect(() => {
|
|
@@ -143,19 +164,19 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
143
164
|
const validMoves = useMemo(() => {
|
|
144
165
|
if (!boardState) return [];
|
|
145
166
|
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
146
|
-
if (sp &&
|
|
167
|
+
if (sp && !sp.hasMovedThisTurn) {
|
|
147
168
|
return getValidMoves(sp, boardState);
|
|
148
169
|
}
|
|
149
170
|
return [];
|
|
150
|
-
}, [boardState, selectedPieceId
|
|
171
|
+
}, [boardState, selectedPieceId]);
|
|
151
172
|
const validPasses = useMemo(() => {
|
|
152
173
|
if (!boardState) return [];
|
|
153
174
|
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
154
|
-
if (sp &&
|
|
175
|
+
if (sp && boardState.ball.holderId === sp.id) {
|
|
155
176
|
return getValidPasses(sp, boardState);
|
|
156
177
|
}
|
|
157
178
|
return [];
|
|
158
|
-
}, [boardState, selectedPieceId
|
|
179
|
+
}, [boardState, selectedPieceId]);
|
|
159
180
|
if (!boardState) return null;
|
|
160
181
|
const lastMove = boardState.lastMove;
|
|
161
182
|
const ball = boardState.ball;
|
|
@@ -175,33 +196,40 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
175
196
|
);
|
|
176
197
|
pickupProportion = totalSteps > 0 ? Math.min(0.85, Math.max(0.15, stepsToBall / totalSteps)) : 0.5;
|
|
177
198
|
}
|
|
199
|
+
const ballCarrier = ball.holderId ? boardState.pieces.find((p) => p.id === ball.holderId) ?? null : null;
|
|
200
|
+
const isOffsideRisk = !!ballCarrier && ballCarrier.type !== "king" && userSide !== null && ballCarrier.side === userSide && boardState.turn === userSide && isInEnemyArea(ballCarrier.pos, ballCarrier.side);
|
|
178
201
|
const handleSquareClick = (x, y) => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
202
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
203
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
204
|
+
if (isValidMove && isValidPass) {
|
|
205
|
+
setDisambiguateAt({ x, y });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (isValidMove && selectedPieceId) {
|
|
209
|
+
movePiece(selectedPieceId, { x, y });
|
|
210
|
+
setDisambiguateAt(null);
|
|
211
|
+
} else if (isValidPass) {
|
|
212
|
+
passBall({ x, y });
|
|
213
|
+
setDisambiguateAt(null);
|
|
214
|
+
} else {
|
|
215
|
+
setDisambiguateAt(null);
|
|
187
216
|
}
|
|
188
217
|
};
|
|
189
218
|
const handlePieceClick = (pieceId, x, y, e) => {
|
|
190
219
|
e.stopPropagation();
|
|
191
220
|
const pieceAt = boardState.pieces.find((p) => p.id === pieceId);
|
|
192
221
|
if (!pieceAt) return;
|
|
193
|
-
const
|
|
194
|
-
|
|
222
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
223
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
224
|
+
if (isValidMove || isValidPass) {
|
|
195
225
|
handleSquareClick(x, y);
|
|
196
226
|
} else if (boardState.turn === pieceAt.side && userSide === pieceAt.side) {
|
|
197
227
|
if (selectedPieceId === pieceAt.id) {
|
|
198
|
-
|
|
199
|
-
setInteractionMode(interactionMode === "move" ? "pass" : "move");
|
|
200
|
-
}
|
|
228
|
+
setSelectedPieceId(null);
|
|
201
229
|
} else {
|
|
202
230
|
setSelectedPieceId(pieceAt.id);
|
|
203
|
-
setInteractionMode("move");
|
|
204
231
|
}
|
|
232
|
+
setDisambiguateAt(null);
|
|
205
233
|
}
|
|
206
234
|
};
|
|
207
235
|
const defaultCursor = () => {
|
|
@@ -230,7 +258,6 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
230
258
|
case "ArrowUp":
|
|
231
259
|
step(0, 1);
|
|
232
260
|
break;
|
|
233
|
-
// visually up = higher rank (y grows toward the top)
|
|
234
261
|
case "ArrowDown":
|
|
235
262
|
step(0, -1);
|
|
236
263
|
break;
|
|
@@ -250,15 +277,25 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
250
277
|
}
|
|
251
278
|
case "m":
|
|
252
279
|
case "M":
|
|
253
|
-
|
|
280
|
+
if (disambiguateAt && selectedPieceId) {
|
|
281
|
+
movePiece(selectedPieceId, disambiguateAt);
|
|
282
|
+
setDisambiguateAt(null);
|
|
283
|
+
}
|
|
254
284
|
break;
|
|
255
285
|
case "p":
|
|
256
286
|
case "P":
|
|
257
|
-
|
|
287
|
+
if (disambiguateAt) {
|
|
288
|
+
passBall(disambiguateAt);
|
|
289
|
+
setDisambiguateAt(null);
|
|
290
|
+
}
|
|
258
291
|
break;
|
|
259
292
|
case "Escape":
|
|
260
|
-
|
|
261
|
-
|
|
293
|
+
if (disambiguateAt) {
|
|
294
|
+
setDisambiguateAt(null);
|
|
295
|
+
} else {
|
|
296
|
+
setSelectedPieceId(null);
|
|
297
|
+
setInteractionMode(null);
|
|
298
|
+
}
|
|
262
299
|
break;
|
|
263
300
|
}
|
|
264
301
|
};
|
|
@@ -268,9 +305,16 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
268
305
|
for (let x = 0; x < COLS; x++) {
|
|
269
306
|
const isGoalArea = x >= 2 && x <= 6 && (y >= 0 && y <= 1 || y >= 10 && y <= 11);
|
|
270
307
|
const isEven = (x + y) % 2 === 0;
|
|
271
|
-
const
|
|
308
|
+
const isValidMove = validMoves.some((m) => m.x === x && m.y === y);
|
|
309
|
+
const isValidPass = validPasses.some((p) => p.x === x && p.y === y);
|
|
310
|
+
const isAmbiguous = isValidMove && isValidPass;
|
|
311
|
+
const isLastMoveOrigin = lastMove?.from && lastMove.from.x === x && lastMove.from.y === y;
|
|
312
|
+
const isLastMoveDest = lastMove?.to && lastMove.to.x === x && lastMove.to.y === y;
|
|
313
|
+
const isLastMove = isLastMoveOrigin || isLastMoveDest;
|
|
314
|
+
const isEnemyGoalArea = userSide !== null && (x >= 2 && x <= 6) && (userSide === "white" ? y >= 10 && y <= 11 : y >= 0 && y <= 1);
|
|
272
315
|
const pieceAt = boardState.pieces.find((p) => p.pos.x === x && p.pos.y === y);
|
|
273
316
|
const isCursor = keyboardNav && cursor?.x === x && cursor?.y === y;
|
|
317
|
+
const isDisambiguateTarget = disambiguateAt?.x === x && disambiguateAt?.y === y;
|
|
274
318
|
squares.push(
|
|
275
319
|
/* @__PURE__ */ jsxs(
|
|
276
320
|
"div",
|
|
@@ -278,16 +322,60 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
278
322
|
onClick: () => handleSquareClick(x, y),
|
|
279
323
|
className: cn(
|
|
280
324
|
"relative aspect-square w-full flex items-center justify-center cursor-pointer",
|
|
281
|
-
isEven ? "bg-
|
|
325
|
+
isEven ? "bg-field-green-1" : "bg-field-green-2",
|
|
282
326
|
isGoalArea && "bg-[#1a3a18] ring-1 ring-inset ring-emerald-500/30",
|
|
283
|
-
|
|
327
|
+
!isValidMove && !isValidPass && isLastMove && "bg-last-move-highlight/20",
|
|
328
|
+
// §8: pulsing warning ring on enemy goal area
|
|
329
|
+
isOffsideRisk && isEnemyGoalArea && !isValidMove && !isValidPass && "ring-2 ring-inset ring-warning/60",
|
|
330
|
+
isAmbiguous && "ring-[3px] ring-inset ring-yellow-400/90 bg-yellow-400/20",
|
|
331
|
+
!isAmbiguous && isValidMove && "ring-[3px] ring-inset ring-yellow-400/80 bg-yellow-400/15",
|
|
332
|
+
!isAmbiguous && isValidPass && "ring-[3px] ring-inset ring-sky-400/80 bg-sky-400/15"
|
|
284
333
|
),
|
|
285
334
|
children: [
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
335
|
+
!isAmbiguous && isValidMove && !pieceAt && /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full bg-yellow-400/65" }),
|
|
336
|
+
!isAmbiguous && isValidPass && !pieceAt && /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-full bg-sky-400/65" }),
|
|
337
|
+
isAmbiguous && !pieceAt && /* @__PURE__ */ jsxs("div", { className: "flex gap-0.5", children: [
|
|
338
|
+
/* @__PURE__ */ jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-yellow-400/80" }),
|
|
339
|
+
/* @__PURE__ */ jsx("div", { className: "w-2.5 h-2.5 rounded-full bg-sky-400/80" })
|
|
340
|
+
] }),
|
|
341
|
+
isCursor && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 ring-2 ring-inset ring-white pointer-events-none z-20", "aria-hidden": "true" }),
|
|
342
|
+
isDisambiguateTarget && selectedPieceId && /* @__PURE__ */ jsxs(
|
|
343
|
+
"div",
|
|
344
|
+
{
|
|
345
|
+
className: "absolute z-50 bottom-full mb-1 left-1/2 -translate-x-1/2 flex gap-1 bg-bg-secondary border border-border-subtle rounded-md p-1 shadow-xl pointer-events-auto whitespace-nowrap",
|
|
346
|
+
onClick: (e) => e.stopPropagation(),
|
|
347
|
+
children: [
|
|
348
|
+
/* @__PURE__ */ jsxs(
|
|
349
|
+
"button",
|
|
350
|
+
{
|
|
351
|
+
className: "flex items-center gap-1 px-2 py-1 rounded text-[11px] font-semibold font-inter text-move-highlight bg-move-highlight/10 hover:bg-move-highlight/20 transition-colors",
|
|
352
|
+
onClick: () => {
|
|
353
|
+
movePiece(selectedPieceId, { x, y });
|
|
354
|
+
setDisambiguateAt(null);
|
|
355
|
+
},
|
|
356
|
+
children: [
|
|
357
|
+
/* @__PURE__ */ jsx(Move, { size: 11, strokeWidth: 2 }),
|
|
358
|
+
t("move")
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
),
|
|
362
|
+
/* @__PURE__ */ jsxs(
|
|
363
|
+
"button",
|
|
364
|
+
{
|
|
365
|
+
className: "flex items-center gap-1 px-2 py-1 rounded text-[11px] font-semibold font-inter text-pass-highlight bg-pass-highlight/10 hover:bg-pass-highlight/20 transition-colors",
|
|
366
|
+
onClick: () => {
|
|
367
|
+
passBall({ x, y });
|
|
368
|
+
setDisambiguateAt(null);
|
|
369
|
+
},
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx("span", { className: "w-3 h-3 inline-flex", children: /* @__PURE__ */ jsx(Football, {}) }),
|
|
372
|
+
t("pass")
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
)
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
)
|
|
291
379
|
]
|
|
292
380
|
},
|
|
293
381
|
`${x}-${y}`
|
|
@@ -301,7 +389,11 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
301
389
|
"div",
|
|
302
390
|
{
|
|
303
391
|
className: cn(
|
|
304
|
-
|
|
392
|
+
// §6: Mobile edge-to-edge — thin 2px margin, no border, no radius
|
|
393
|
+
"w-full mx-0.5 bg-[#1a3317] overflow-hidden",
|
|
394
|
+
// §5+§6: Desktop — centered, rounded, bordered, height-based max-width
|
|
395
|
+
"md:mx-auto md:rounded-xl md:shadow-2xl md:border-4 md:border-[#1a3317]",
|
|
396
|
+
"md:max-w-[min(700px,calc((100dvh_-_150px)*0.75))]",
|
|
305
397
|
keyboardNav && "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-green focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary"
|
|
306
398
|
),
|
|
307
399
|
...keyboardNav ? {
|
|
@@ -336,12 +428,7 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
336
428
|
/* @__PURE__ */ jsxs("div", { className: "absolute inset-0 pointer-events-none", children: [
|
|
337
429
|
/* @__PURE__ */ jsx(AnimatePresence, { mode: "sync", children: boardState.pieces.map((piece) => {
|
|
338
430
|
const isPickupCarrier = pickupCarrierId === piece.id;
|
|
339
|
-
const pieceTransition = isPickupCarrier ? { duration: PICKUP_DURATION, ease: "linear" } : {
|
|
340
|
-
type: "spring",
|
|
341
|
-
stiffness: 200,
|
|
342
|
-
damping: 25,
|
|
343
|
-
mass: 1
|
|
344
|
-
};
|
|
431
|
+
const pieceTransition = isPickupCarrier ? { duration: PICKUP_DURATION, ease: "linear" } : { type: "spring", stiffness: 200, damping: 25, mass: 1 };
|
|
345
432
|
return /* @__PURE__ */ jsx(
|
|
346
433
|
motion.div,
|
|
347
434
|
{
|
|
@@ -385,23 +472,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
385
472
|
top: [`${prevY}%`, `${prevY}%`, `${prevY}%`, `${targetY}%`],
|
|
386
473
|
scale: [1, 1, 1.35, 1]
|
|
387
474
|
};
|
|
388
|
-
transition = {
|
|
389
|
-
duration: PICKUP_DURATION,
|
|
390
|
-
times: [0, pulseStart, pickupProportion, 1],
|
|
391
|
-
ease: "linear"
|
|
392
|
-
};
|
|
475
|
+
transition = { duration: PICKUP_DURATION, times: [0, pulseStart, pickupProportion, 1], ease: "linear" };
|
|
393
476
|
} else {
|
|
394
|
-
animate = {
|
|
395
|
-
|
|
396
|
-
top: `${targetY}%`,
|
|
397
|
-
scale: 1
|
|
398
|
-
};
|
|
399
|
-
transition = {
|
|
400
|
-
type: "spring",
|
|
401
|
-
stiffness: 300,
|
|
402
|
-
damping: 30,
|
|
403
|
-
mass: 0.5
|
|
404
|
-
};
|
|
477
|
+
animate = { left: `${targetX}%`, top: `${targetY}%`, scale: 1 };
|
|
478
|
+
transition = { type: "spring", stiffness: 300, damping: 30, mass: 0.5 };
|
|
405
479
|
}
|
|
406
480
|
return /* @__PURE__ */ jsx(
|
|
407
481
|
motion.div,
|
|
@@ -421,13 +495,17 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
421
495
|
"ball"
|
|
422
496
|
);
|
|
423
497
|
})()
|
|
498
|
+
] }),
|
|
499
|
+
isOffsideRisk && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 z-30 flex items-center gap-1.5 px-3 py-1.5 bg-warning/20 border border-warning/50 rounded-full font-inter text-xs font-semibold text-warning pointer-events-none whitespace-nowrap backdrop-blur-sm", children: [
|
|
500
|
+
"\u26A0 ",
|
|
501
|
+
t("offsideWarning")
|
|
424
502
|
] })
|
|
425
503
|
] })
|
|
426
504
|
}
|
|
427
505
|
);
|
|
428
506
|
if (!showCoordinates) return board;
|
|
429
507
|
const ranksTopToBottom = [...RANK_LABELS].reverse();
|
|
430
|
-
return /* @__PURE__ */ jsx("div", { className: "w-full max-w-[540px] mx-auto", "aria-hidden": "false", children: /* @__PURE__ */ jsxs(
|
|
508
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full max-w-[540px] md:max-w-none mx-auto", "aria-hidden": "false", children: /* @__PURE__ */ jsxs(
|
|
431
509
|
"div",
|
|
432
510
|
{
|
|
433
511
|
className: "grid gap-1 select-none",
|
|
@@ -450,26 +528,10 @@ function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
|
450
528
|
) });
|
|
451
529
|
}
|
|
452
530
|
function FileRow({ files }) {
|
|
453
|
-
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-9 gap-[1px] px-[2px]", children: files.map((f) => /* @__PURE__ */ jsx(
|
|
454
|
-
"span",
|
|
455
|
-
{
|
|
456
|
-
"aria-hidden": "true",
|
|
457
|
-
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 uppercase tracking-[0.5px]",
|
|
458
|
-
children: f
|
|
459
|
-
},
|
|
460
|
-
f
|
|
461
|
-
)) });
|
|
531
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-9 gap-[1px] px-[2px]", children: files.map((f) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 uppercase tracking-[0.5px]", children: f }, f)) });
|
|
462
532
|
}
|
|
463
533
|
function RankColumn({ ranks }) {
|
|
464
|
-
return /* @__PURE__ */ jsx("div", { className: "grid grid-rows-12 gap-[1px] py-[2px]", children: ranks.map((r) => /* @__PURE__ */ jsx(
|
|
465
|
-
"span",
|
|
466
|
-
{
|
|
467
|
-
"aria-hidden": "true",
|
|
468
|
-
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 tabular-nums",
|
|
469
|
-
children: r
|
|
470
|
-
},
|
|
471
|
-
r
|
|
472
|
-
)) });
|
|
534
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-rows-12 gap-[1px] py-[2px]", children: ranks.map((r) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 tabular-nums", children: r }, r)) });
|
|
473
535
|
}
|
|
474
536
|
function Avatar({ className, children }) {
|
|
475
537
|
return /* @__PURE__ */ jsx("div", { className: cn("relative w-9 h-9 rounded-full overflow-hidden shrink-0", className), children });
|
|
@@ -481,14 +543,6 @@ function AvatarImage({ src, alt, className }) {
|
|
|
481
543
|
function AvatarFallback({ className, children }) {
|
|
482
544
|
return /* @__PURE__ */ jsx("div", { className: cn("absolute inset-0 flex items-center justify-center text-xs font-semibold", className), children });
|
|
483
545
|
}
|
|
484
|
-
var fallback = (key) => key;
|
|
485
|
-
var GameI18nContext = createContext(fallback);
|
|
486
|
-
function GameI18nProvider({ t, children }) {
|
|
487
|
-
return /* @__PURE__ */ jsx(GameI18nContext.Provider, { value: t, children });
|
|
488
|
-
}
|
|
489
|
-
function useGameT() {
|
|
490
|
-
return useContext(GameI18nContext);
|
|
491
|
-
}
|
|
492
546
|
function ActionPoints({ total, remaining, size = 20, className, kingMustRelease }) {
|
|
493
547
|
const t = useGameT();
|
|
494
548
|
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-1.5", className), "aria-label": t("actionPointsAriaLabel", { remaining, total }), children: Array.from({ length: total }).map((_, i) => {
|
|
@@ -546,6 +600,7 @@ function Scoreboard({
|
|
|
546
600
|
const isMyTurn = turn === userSide;
|
|
547
601
|
const myKingMustRelease = isMyTurn && kingMustRelease === turn;
|
|
548
602
|
const rivalKingMustRelease = !isMyTurn && kingMustRelease === turn;
|
|
603
|
+
const maxAP = boardState.maxActionPoints ?? 5;
|
|
549
604
|
return /* @__PURE__ */ jsxs("div", { className: "w-full bg-bg-secondary flex items-center justify-between px-5 py-2.5", children: [
|
|
550
605
|
/* @__PURE__ */ jsx(
|
|
551
606
|
PlayerInfo,
|
|
@@ -555,6 +610,7 @@ function Scoreboard({
|
|
|
555
610
|
avatarUrl: myAvatar,
|
|
556
611
|
isActive: isMyTurn,
|
|
557
612
|
actionPoints: isMyTurn ? actionPoints : 0,
|
|
613
|
+
maxActionPoints: maxAP,
|
|
558
614
|
kingMustRelease: myKingMustRelease,
|
|
559
615
|
align: "left"
|
|
560
616
|
}
|
|
@@ -572,13 +628,14 @@ function Scoreboard({
|
|
|
572
628
|
avatarUrl: rivalAvatar,
|
|
573
629
|
isActive: !isMyTurn,
|
|
574
630
|
actionPoints: !isMyTurn ? actionPoints : 0,
|
|
631
|
+
maxActionPoints: maxAP,
|
|
575
632
|
kingMustRelease: rivalKingMustRelease,
|
|
576
633
|
align: "right"
|
|
577
634
|
}
|
|
578
635
|
)
|
|
579
636
|
] });
|
|
580
637
|
}
|
|
581
|
-
function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, kingMustRelease, align }) {
|
|
638
|
+
function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, maxActionPoints, kingMustRelease, align }) {
|
|
582
639
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2.5", align === "right" && "flex-row-reverse"), children: [
|
|
583
640
|
/* @__PURE__ */ jsxs(Avatar, { className: cn(
|
|
584
641
|
"w-9 h-9 border-2 transition-colors duration-300",
|
|
@@ -599,7 +656,7 @@ function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, kingMustRel
|
|
|
599
656
|
/* @__PURE__ */ jsx(
|
|
600
657
|
ActionPoints,
|
|
601
658
|
{
|
|
602
|
-
total:
|
|
659
|
+
total: maxActionPoints,
|
|
603
660
|
remaining: actionPoints,
|
|
604
661
|
size: 12,
|
|
605
662
|
kingMustRelease,
|
|
@@ -720,78 +777,36 @@ function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, on
|
|
|
720
777
|
function GameControls({ isMyTurn }) {
|
|
721
778
|
const {
|
|
722
779
|
boardState,
|
|
723
|
-
selectedPieceId,
|
|
724
|
-
interactionMode,
|
|
725
|
-
setInteractionMode,
|
|
726
780
|
endTurn
|
|
727
781
|
} = useGameStore();
|
|
728
782
|
const t = useGameT();
|
|
729
783
|
const [showEndTurnConfirm, setShowEndTurnConfirm] = useState(false);
|
|
730
784
|
if (!boardState) return null;
|
|
731
785
|
const actionPoints = boardState.actionPoints;
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
786
|
+
const handleEndTurn = () => {
|
|
787
|
+
if (actionPoints >= 2) {
|
|
788
|
+
setShowEndTurnConfirm(true);
|
|
789
|
+
} else {
|
|
790
|
+
endTurn();
|
|
791
|
+
}
|
|
792
|
+
};
|
|
736
793
|
return /* @__PURE__ */ jsxs("div", { className: "w-full bg-bg-secondary flex flex-col gap-3 px-5 pt-3 pb-5", children: [
|
|
737
|
-
/* @__PURE__ */ jsx(AnimatePresence, { children: isMyTurn && /* @__PURE__ */ jsxs(
|
|
738
|
-
motion.div,
|
|
739
|
-
{
|
|
740
|
-
initial: { opacity: 0, y: 8 },
|
|
741
|
-
animate: { opacity: 1, y: 0 },
|
|
742
|
-
exit: { opacity: 0, y: 8 },
|
|
743
|
-
className: "flex gap-2",
|
|
744
|
-
children: [
|
|
745
|
-
/* @__PURE__ */ jsxs(
|
|
746
|
-
"button",
|
|
747
|
-
{
|
|
748
|
-
onClick: () => setInteractionMode("move"),
|
|
749
|
-
disabled: !canMove,
|
|
750
|
-
className: cn(
|
|
751
|
-
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
752
|
-
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-move-highlight",
|
|
753
|
-
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
754
|
-
interactionMode === "move" ? "bg-move-highlight/10 border-move-highlight text-move-highlight" : "bg-bg-surface border-border-subtle text-move-highlight hover:bg-move-highlight/5"
|
|
755
|
-
),
|
|
756
|
-
"aria-pressed": interactionMode === "move",
|
|
757
|
-
children: [
|
|
758
|
-
/* @__PURE__ */ jsx(Move, { size: 16, strokeWidth: 2 }),
|
|
759
|
-
t("move")
|
|
760
|
-
]
|
|
761
|
-
}
|
|
762
|
-
),
|
|
763
|
-
/* @__PURE__ */ jsxs(
|
|
764
|
-
"button",
|
|
765
|
-
{
|
|
766
|
-
onClick: () => setInteractionMode("pass"),
|
|
767
|
-
disabled: !canPass,
|
|
768
|
-
className: cn(
|
|
769
|
-
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
770
|
-
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pass-highlight",
|
|
771
|
-
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
772
|
-
interactionMode === "pass" ? "bg-pass-highlight/10 border-pass-highlight text-pass-highlight" : "bg-bg-surface border-border-subtle text-pass-highlight hover:bg-pass-highlight/5"
|
|
773
|
-
),
|
|
774
|
-
"aria-pressed": interactionMode === "pass",
|
|
775
|
-
children: [
|
|
776
|
-
/* @__PURE__ */ jsx("span", { className: "w-4 h-4 inline-flex", children: /* @__PURE__ */ jsx(Football, {}) }),
|
|
777
|
-
t("pass")
|
|
778
|
-
]
|
|
779
|
-
}
|
|
780
|
-
)
|
|
781
|
-
]
|
|
782
|
-
},
|
|
783
|
-
"action-btns"
|
|
784
|
-
) }),
|
|
785
794
|
isMyTurn && /* @__PURE__ */ jsxs(
|
|
786
795
|
Button,
|
|
787
796
|
{
|
|
788
797
|
variant: "primary",
|
|
789
798
|
size: "default",
|
|
790
799
|
className: "w-full gap-2 tracking-[1.5px]",
|
|
791
|
-
onClick:
|
|
800
|
+
onClick: handleEndTurn,
|
|
792
801
|
children: [
|
|
793
802
|
/* @__PURE__ */ jsx(Flag, { size: 16, strokeWidth: 2 }),
|
|
794
|
-
t("endTurn")
|
|
803
|
+
t("endTurn"),
|
|
804
|
+
actionPoints >= 2 && /* @__PURE__ */ jsxs("span", { className: cn("ml-1 font-inter text-xs font-normal opacity-70 tracking-normal"), children: [
|
|
805
|
+
"\xB7 ",
|
|
806
|
+
actionPoints,
|
|
807
|
+
" ",
|
|
808
|
+
t("actionPointsShort")
|
|
809
|
+
] })
|
|
795
810
|
]
|
|
796
811
|
}
|
|
797
812
|
),
|
|
@@ -1082,7 +1097,194 @@ function StaticGameBoard() {
|
|
|
1082
1097
|
)
|
|
1083
1098
|
] });
|
|
1084
1099
|
}
|
|
1100
|
+
function TurnBanner({ isMyTurn, waitingLabel, className }) {
|
|
1101
|
+
const t = useGameT();
|
|
1102
|
+
const { boardState } = useGameStore();
|
|
1103
|
+
if (!boardState) return null;
|
|
1104
|
+
const { actionPoints, maxActionPoints } = boardState;
|
|
1105
|
+
return /* @__PURE__ */ jsxs(
|
|
1106
|
+
"div",
|
|
1107
|
+
{
|
|
1108
|
+
className: cn(
|
|
1109
|
+
"w-full h-9 flex items-center justify-between px-4 shrink-0 transition-colors duration-300",
|
|
1110
|
+
isMyTurn ? "bg-accent-green/15 border-b border-accent-green/30" : "bg-bg-surface border-b border-border-subtle",
|
|
1111
|
+
className
|
|
1112
|
+
),
|
|
1113
|
+
"aria-live": "polite",
|
|
1114
|
+
"aria-atomic": "true",
|
|
1115
|
+
children: [
|
|
1116
|
+
/* @__PURE__ */ jsx(
|
|
1117
|
+
"span",
|
|
1118
|
+
{
|
|
1119
|
+
className: cn(
|
|
1120
|
+
"font-anton text-sm tracking-[1.5px] uppercase",
|
|
1121
|
+
isMyTurn ? "text-accent-green" : "text-fg-muted"
|
|
1122
|
+
),
|
|
1123
|
+
children: isMyTurn ? t("yourTurn") : waitingLabel ?? t("waitingRival")
|
|
1124
|
+
}
|
|
1125
|
+
),
|
|
1126
|
+
isMyTurn && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", "aria-label": t("actionPointsAriaLabel", { remaining: actionPoints, total: maxActionPoints }), children: [
|
|
1127
|
+
/* @__PURE__ */ jsx(Zap, { size: 12, className: "text-accent-green", "aria-hidden": "true" }),
|
|
1128
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-fg-secondary tabular-nums", children: [
|
|
1129
|
+
actionPoints,
|
|
1130
|
+
"/",
|
|
1131
|
+
maxActionPoints
|
|
1132
|
+
] })
|
|
1133
|
+
] })
|
|
1134
|
+
]
|
|
1135
|
+
}
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
var TOASTABLE = /* @__PURE__ */ new Set(["interception", "offside", "tackle"]);
|
|
1139
|
+
var TOAST_CONFIG = {
|
|
1140
|
+
interception: "bg-danger/20 border-danger/50 text-danger",
|
|
1141
|
+
offside: "bg-warning/20 border-warning/50 text-warning",
|
|
1142
|
+
tackle: "bg-warning/20 border-warning/50 text-warning"
|
|
1143
|
+
};
|
|
1144
|
+
function EventToast({ className }) {
|
|
1145
|
+
const t = useGameT();
|
|
1146
|
+
const { boardState } = useGameStore();
|
|
1147
|
+
const [toast, setToast] = useState(null);
|
|
1148
|
+
useEffect(() => {
|
|
1149
|
+
const lastMove = boardState?.lastMove;
|
|
1150
|
+
if (!lastMove) return;
|
|
1151
|
+
if (!TOASTABLE.has(lastMove.type)) return;
|
|
1152
|
+
setToast({ type: lastMove.type, key: String(lastMove.at) });
|
|
1153
|
+
}, [boardState?.lastMove?.at]);
|
|
1154
|
+
useEffect(() => {
|
|
1155
|
+
if (!toast) return;
|
|
1156
|
+
const timer = setTimeout(() => setToast(null), 2500);
|
|
1157
|
+
return () => clearTimeout(timer);
|
|
1158
|
+
}, [toast?.key]);
|
|
1159
|
+
return /* @__PURE__ */ jsx(
|
|
1160
|
+
"div",
|
|
1161
|
+
{
|
|
1162
|
+
role: "status",
|
|
1163
|
+
"aria-live": "polite",
|
|
1164
|
+
"aria-atomic": "true",
|
|
1165
|
+
className: cn("pointer-events-none flex justify-center", className),
|
|
1166
|
+
children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: toast && /* @__PURE__ */ jsx(
|
|
1167
|
+
motion.div,
|
|
1168
|
+
{
|
|
1169
|
+
initial: { opacity: 0, y: -8 },
|
|
1170
|
+
animate: { opacity: 1, y: 0 },
|
|
1171
|
+
exit: { opacity: 0, y: -8 },
|
|
1172
|
+
transition: { duration: 0.15 },
|
|
1173
|
+
className: cn(
|
|
1174
|
+
"px-4 py-2 rounded-full border font-inter text-sm font-semibold shadow-lg backdrop-blur-sm text-center",
|
|
1175
|
+
TOAST_CONFIG[toast.type]
|
|
1176
|
+
),
|
|
1177
|
+
children: t(`eventToast.${toast.type}`)
|
|
1178
|
+
},
|
|
1179
|
+
toast.key
|
|
1180
|
+
) })
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
function MobileHistory({ className }) {
|
|
1185
|
+
const { boardState } = useGameStore();
|
|
1186
|
+
const [open, setOpen] = useState(false);
|
|
1187
|
+
const lastMove = boardState?.moveHistory?.at(-1);
|
|
1188
|
+
if (!lastMove) return null;
|
|
1189
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1190
|
+
/* @__PURE__ */ jsx(HistoryChip, { lastMove, onClick: () => setOpen(true), className }),
|
|
1191
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx(
|
|
1193
|
+
motion.div,
|
|
1194
|
+
{
|
|
1195
|
+
initial: { opacity: 0 },
|
|
1196
|
+
animate: { opacity: 1 },
|
|
1197
|
+
exit: { opacity: 0 },
|
|
1198
|
+
transition: { duration: 0.2 },
|
|
1199
|
+
className: "fixed inset-0 z-40 bg-black/60 backdrop-blur-sm",
|
|
1200
|
+
onClick: () => setOpen(false),
|
|
1201
|
+
"aria-hidden": "true"
|
|
1202
|
+
},
|
|
1203
|
+
"backdrop"
|
|
1204
|
+
),
|
|
1205
|
+
/* @__PURE__ */ jsxs(
|
|
1206
|
+
motion.div,
|
|
1207
|
+
{
|
|
1208
|
+
initial: { y: "100%" },
|
|
1209
|
+
animate: { y: 0 },
|
|
1210
|
+
exit: { y: "100%" },
|
|
1211
|
+
transition: { type: "spring", stiffness: 300, damping: 32 },
|
|
1212
|
+
className: "fixed bottom-0 left-0 right-0 z-50 bg-bg-secondary rounded-t-2xl border-t border-border-subtle shadow-xl",
|
|
1213
|
+
role: "dialog",
|
|
1214
|
+
"aria-modal": "true",
|
|
1215
|
+
children: [
|
|
1216
|
+
/* @__PURE__ */ jsx(SheetHeader, { onClose: () => setOpen(false) }),
|
|
1217
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 pb-6 max-h-[50dvh] overflow-y-auto", children: /* @__PURE__ */ jsx(MoveHistory, { scrollable: false }) })
|
|
1218
|
+
]
|
|
1219
|
+
},
|
|
1220
|
+
"sheet"
|
|
1221
|
+
)
|
|
1222
|
+
] }) })
|
|
1223
|
+
] });
|
|
1224
|
+
}
|
|
1225
|
+
function SheetHeader({ onClose }) {
|
|
1226
|
+
const t = useGameT();
|
|
1227
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 pt-4 pb-3 border-b border-border-subtle", children: [
|
|
1228
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] tracking-[0.5px] uppercase text-fg-muted", children: t("history.title") }),
|
|
1229
|
+
/* @__PURE__ */ jsx(
|
|
1230
|
+
"button",
|
|
1231
|
+
{
|
|
1232
|
+
onClick: onClose,
|
|
1233
|
+
className: "w-8 h-8 flex items-center justify-center rounded-full bg-bg-surface hover:bg-bg-surface-elevated text-fg-muted hover:text-fg-primary transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green",
|
|
1234
|
+
"aria-label": "Close",
|
|
1235
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1236
|
+
}
|
|
1237
|
+
)
|
|
1238
|
+
] });
|
|
1239
|
+
}
|
|
1240
|
+
function HistoryChip({ lastMove, onClick, className }) {
|
|
1241
|
+
const t = useGameT();
|
|
1242
|
+
const toSq = squareName(lastMove.to);
|
|
1243
|
+
let label;
|
|
1244
|
+
switch (lastMove.type) {
|
|
1245
|
+
case "pass":
|
|
1246
|
+
label = `\u2295 ${toSq}`;
|
|
1247
|
+
break;
|
|
1248
|
+
case "tackle":
|
|
1249
|
+
label = `\u2715 ${toSq}`;
|
|
1250
|
+
break;
|
|
1251
|
+
case "interception":
|
|
1252
|
+
label = `\u26D4 ${toSq}`;
|
|
1253
|
+
break;
|
|
1254
|
+
case "goal":
|
|
1255
|
+
label = `\u26BD GOL`;
|
|
1256
|
+
break;
|
|
1257
|
+
case "offside":
|
|
1258
|
+
label = `\u26A0 OFS`;
|
|
1259
|
+
break;
|
|
1260
|
+
default: {
|
|
1261
|
+
const pieceShort = t(`pieces.${SHORT_KEY[lastMove.pieceType]}`);
|
|
1262
|
+
const fromSq = lastMove.from ? squareName(lastMove.from) : "";
|
|
1263
|
+
label = fromSq ? `${pieceShort}${fromSq}\u2192${toSq}` : `${pieceShort}\u2192${toSq}`;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return /* @__PURE__ */ jsxs(
|
|
1267
|
+
"button",
|
|
1268
|
+
{
|
|
1269
|
+
onClick,
|
|
1270
|
+
className: cn(
|
|
1271
|
+
"flex items-center gap-1.5 px-3 py-1.5 rounded-full",
|
|
1272
|
+
"bg-bg-surface border border-border-subtle",
|
|
1273
|
+
"font-mono text-[11px] text-fg-secondary",
|
|
1274
|
+
"hover:text-fg-primary hover:bg-bg-surface-elevated active:scale-95",
|
|
1275
|
+
"transition-all duration-150",
|
|
1276
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green",
|
|
1277
|
+
className
|
|
1278
|
+
),
|
|
1279
|
+
"aria-label": t("history.viewAll"),
|
|
1280
|
+
children: [
|
|
1281
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
1282
|
+
/* @__PURE__ */ jsx("span", { className: "text-fg-muted text-[10px]", "aria-hidden": "true", children: "\u25BE" })
|
|
1283
|
+
]
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1085
1287
|
|
|
1086
|
-
export { ActionPoints, Avatar, AvatarFallback, AvatarImage, Button, ConfirmDialog, Football, GameBoard, GameControls, GameI18nProvider, GamePiece, MiniScoreboard, MoveHistory, Scoreboard, SelectedPieceDetail, StaticGameBoard, cn, useGameT };
|
|
1288
|
+
export { ActionPoints, Avatar, AvatarFallback, AvatarImage, Button, ConfirmDialog, EventToast, Football, GameBoard, GameControls, GameI18nProvider, GamePiece, MiniScoreboard, MobileHistory, MoveHistory, Scoreboard, SelectedPieceDetail, StaticGameBoard, TurnBanner, cn, useGameT };
|
|
1087
1289
|
//# sourceMappingURL=index.js.map
|
|
1088
1290
|
//# sourceMappingURL=index.js.map
|