@stargazeryan/poker-ui 0.4.3 → 0.6.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/README.md +205 -12
- package/dist/index.cjs +746 -175
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +547 -23
- package/dist/index.d.ts +547 -23
- package/dist/index.js +696 -168
- package/dist/index.js.map +1 -1
- package/package.json +27 -7
- package/src/styles.css +128 -95
package/dist/index.cjs
CHANGED
|
@@ -22,19 +22,62 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
BB: () => BB,
|
|
25
|
+
COUNT_UP_MS: () => COUNT_UP_MS,
|
|
25
26
|
CardBack: () => CardBack,
|
|
26
27
|
CardTable: () => CardTable,
|
|
28
|
+
Chip: () => Chip,
|
|
29
|
+
ChipStack: () => ChipStack,
|
|
27
30
|
CommunityCards: () => CommunityCards,
|
|
31
|
+
DEAL_STAGGER_MS: () => DEAL_STAGGER_MS,
|
|
32
|
+
DEFAULT_BET_SCALE: () => DEFAULT_BET_SCALE,
|
|
33
|
+
DEFAULT_DENOMINATIONS: () => DEFAULT_DENOMINATIONS,
|
|
34
|
+
DEFAULT_FELT_GAP: () => DEFAULT_FELT_GAP,
|
|
35
|
+
DEFAULT_MARGIN_X: () => DEFAULT_MARGIN_X,
|
|
36
|
+
DEFAULT_MARGIN_Y: () => DEFAULT_MARGIN_Y,
|
|
37
|
+
DEFAULT_SEAT_GAP: () => DEFAULT_SEAT_GAP,
|
|
38
|
+
DEFAULT_SEAT_H: () => DEFAULT_SEAT_H,
|
|
39
|
+
DEFAULT_SEAT_W: () => DEFAULT_SEAT_W,
|
|
40
|
+
DESIGN_H: () => DESIGN_H,
|
|
41
|
+
DESIGN_W: () => DESIGN_W,
|
|
42
|
+
FLIP_STAGGER_MS: () => FLIP_STAGGER_MS,
|
|
43
|
+
GLOW_GAP: () => GLOW_GAP,
|
|
28
44
|
HoleCards: () => HoleCards,
|
|
29
45
|
PlayingCard: () => PlayingCard,
|
|
30
46
|
PokerTable: () => CardTable,
|
|
31
47
|
Pot: () => Pot,
|
|
48
|
+
RIM_GAP: () => RIM_GAP,
|
|
49
|
+
SEAT_SHRINK_FLOOR: () => SEAT_SHRINK_FLOOR,
|
|
50
|
+
SIZES: () => SIZES,
|
|
32
51
|
Seat: () => Seat,
|
|
52
|
+
chipBreakdown: () => chipBreakdown,
|
|
53
|
+
chipCount: () => chipCount,
|
|
54
|
+
chipSpotColor: () => chipSpotColor,
|
|
55
|
+
chipsTotal: () => chipsTotal,
|
|
33
56
|
cn: () => cn,
|
|
57
|
+
contrastRatio: () => contrastRatio,
|
|
58
|
+
describeCard: () => describeCard,
|
|
59
|
+
ellipseBox: () => ellipseBox,
|
|
60
|
+
ellipsePoint: () => ellipsePoint,
|
|
61
|
+
fitSeatScale: () => fitSeatScale,
|
|
62
|
+
flatten: () => flatten,
|
|
34
63
|
formatBB: () => formatBB,
|
|
64
|
+
getRankName: () => getRankName,
|
|
65
|
+
getSuitName: () => getSuitName,
|
|
35
66
|
getSuitSymbol: () => getSuitSymbol,
|
|
67
|
+
isKnownSuit: () => isKnownSuit,
|
|
68
|
+
isRedSuit: () => isRedSuit,
|
|
69
|
+
layoutSeats: () => layoutSeats,
|
|
70
|
+
minSeatSeparation: () => minSeatSeparation,
|
|
71
|
+
mostVisible: () => mostVisible,
|
|
36
72
|
nextSeatIndex: () => nextSeatIndex,
|
|
37
|
-
parseCard: () => parseCard
|
|
73
|
+
parseCard: () => parseCard,
|
|
74
|
+
parseColor: () => parseColor,
|
|
75
|
+
relativeLuminance: () => relativeLuminance,
|
|
76
|
+
ringPoints: () => ringPoints,
|
|
77
|
+
seatAngle: () => seatAngle,
|
|
78
|
+
seatPoint: () => seatPoint,
|
|
79
|
+
seatRingRadii: () => seatRingRadii,
|
|
80
|
+
tableRadii: () => tableRadii
|
|
38
81
|
});
|
|
39
82
|
module.exports = __toCommonJS(index_exports);
|
|
40
83
|
|
|
@@ -46,17 +89,147 @@ function cn(...parts) {
|
|
|
46
89
|
|
|
47
90
|
// src/format.ts
|
|
48
91
|
var BB = 20;
|
|
49
|
-
function formatBB(amount) {
|
|
50
|
-
const
|
|
51
|
-
return `${Number.isInteger(
|
|
92
|
+
function formatBB(amount, bb = BB) {
|
|
93
|
+
const value = amount / bb;
|
|
94
|
+
return `${Number.isInteger(value) ? value : value.toFixed(1)} BB`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/color.ts
|
|
98
|
+
function parseColor(color) {
|
|
99
|
+
const input = color.trim();
|
|
100
|
+
const hex = input.match(/^#([0-9a-f]{3,8})$/i);
|
|
101
|
+
if (hex) {
|
|
102
|
+
const raw = hex[1];
|
|
103
|
+
if (raw.length === 3 || raw.length === 4) {
|
|
104
|
+
const [r, g, b, a] = raw.split("").map((c) => parseInt(c + c, 16));
|
|
105
|
+
return { r, g, b, a: raw.length === 4 ? a / 255 : 1 };
|
|
106
|
+
}
|
|
107
|
+
if (raw.length === 6 || raw.length === 8) {
|
|
108
|
+
return {
|
|
109
|
+
r: parseInt(raw.slice(0, 2), 16),
|
|
110
|
+
g: parseInt(raw.slice(2, 4), 16),
|
|
111
|
+
b: parseInt(raw.slice(4, 6), 16),
|
|
112
|
+
a: raw.length === 8 ? parseInt(raw.slice(6, 8), 16) / 255 : 1
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const fn = input.match(/^rgba?\(([^)]+)\)$/i);
|
|
118
|
+
if (!fn) return null;
|
|
119
|
+
const parts = fn[1].split(/[,\s/]+/).filter(Boolean).map(Number);
|
|
120
|
+
if (parts.length < 3 || parts.slice(0, 3).some((n) => !Number.isFinite(n))) return null;
|
|
121
|
+
return { r: parts[0], g: parts[1], b: parts[2], a: Number.isFinite(parts[3]) ? parts[3] : 1 };
|
|
122
|
+
}
|
|
123
|
+
function relativeLuminance(color) {
|
|
124
|
+
const parsed = parseColor(color);
|
|
125
|
+
if (!parsed) return null;
|
|
126
|
+
const channel = (c) => {
|
|
127
|
+
const s = Math.min(255, Math.max(0, c)) / 255;
|
|
128
|
+
return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
129
|
+
};
|
|
130
|
+
return 0.2126 * channel(parsed.r) + 0.7152 * channel(parsed.g) + 0.0722 * channel(parsed.b);
|
|
131
|
+
}
|
|
132
|
+
function flatten(foreground, background) {
|
|
133
|
+
const fg = parseColor(foreground);
|
|
134
|
+
const bg = parseColor(background);
|
|
135
|
+
if (!fg || !bg) return null;
|
|
136
|
+
const mix = (f, b) => fg.a * f + (1 - fg.a) * b;
|
|
137
|
+
return { r: mix(fg.r, bg.r), g: mix(fg.g, bg.g), b: mix(fg.b, bg.b), a: 1 };
|
|
138
|
+
}
|
|
139
|
+
function contrastRatio(foreground, background) {
|
|
140
|
+
const composited = flatten(foreground, background);
|
|
141
|
+
if (!composited) return null;
|
|
142
|
+
const a = relativeLuminance(`rgb(${composited.r}, ${composited.g}, ${composited.b})`);
|
|
143
|
+
const b = relativeLuminance(background);
|
|
144
|
+
if (a === null || b === null) return null;
|
|
145
|
+
const [hi, lo] = a > b ? [a, b] : [b, a];
|
|
146
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
147
|
+
}
|
|
148
|
+
function mostVisible(candidates, background) {
|
|
149
|
+
let best = candidates[0];
|
|
150
|
+
let bestRatio = -1;
|
|
151
|
+
for (const candidate of candidates) {
|
|
152
|
+
const ratio = contrastRatio(candidate, background);
|
|
153
|
+
if (ratio !== null && ratio > bestRatio) {
|
|
154
|
+
best = candidate;
|
|
155
|
+
bestRatio = ratio;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return best;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/sizes.ts
|
|
162
|
+
var SIZES = ["xs", "sm", "md", "lg"];
|
|
163
|
+
|
|
164
|
+
// src/motion.ts
|
|
165
|
+
var COUNT_UP_MS = 500;
|
|
166
|
+
var DEAL_STAGGER_MS = 100;
|
|
167
|
+
var FLIP_STAGGER_MS = 70;
|
|
168
|
+
|
|
169
|
+
// src/cards.ts
|
|
170
|
+
var RANK_NAMES = {
|
|
171
|
+
A: "Ace",
|
|
172
|
+
K: "King",
|
|
173
|
+
Q: "Queen",
|
|
174
|
+
J: "Jack",
|
|
175
|
+
"10": "Ten",
|
|
176
|
+
"9": "Nine",
|
|
177
|
+
"8": "Eight",
|
|
178
|
+
"7": "Seven",
|
|
179
|
+
"6": "Six",
|
|
180
|
+
"5": "Five",
|
|
181
|
+
"4": "Four",
|
|
182
|
+
"3": "Three",
|
|
183
|
+
"2": "Two"
|
|
184
|
+
};
|
|
185
|
+
var SUIT_NAMES = {
|
|
186
|
+
s: "spades",
|
|
187
|
+
h: "hearts",
|
|
188
|
+
d: "diamonds",
|
|
189
|
+
c: "clubs"
|
|
190
|
+
};
|
|
191
|
+
var SUIT_SYMBOLS = {
|
|
192
|
+
s: "\u2660",
|
|
193
|
+
h: "\u2665",
|
|
194
|
+
d: "\u2666",
|
|
195
|
+
c: "\u2663"
|
|
196
|
+
};
|
|
197
|
+
function parseCard(card) {
|
|
198
|
+
if (!card || card.length < 2) return { rank: "", suit: "" };
|
|
199
|
+
return { rank: card.slice(0, -1), suit: card.slice(-1).toLowerCase() };
|
|
200
|
+
}
|
|
201
|
+
function getSuitSymbol(suit) {
|
|
202
|
+
return SUIT_SYMBOLS[suit.toLowerCase()] ?? suit;
|
|
203
|
+
}
|
|
204
|
+
function getRankName(rank) {
|
|
205
|
+
return RANK_NAMES[rank.toUpperCase()] ?? rank;
|
|
206
|
+
}
|
|
207
|
+
function getSuitName(suit) {
|
|
208
|
+
return SUIT_NAMES[suit.toLowerCase()] ?? suit;
|
|
209
|
+
}
|
|
210
|
+
function isRedSuit(suit) {
|
|
211
|
+
const s = suit.toLowerCase();
|
|
212
|
+
return s === "h" || s === "d";
|
|
213
|
+
}
|
|
214
|
+
function isKnownSuit(suit) {
|
|
215
|
+
return suit.toLowerCase() in SUIT_SYMBOLS;
|
|
216
|
+
}
|
|
217
|
+
function describeCard(card) {
|
|
218
|
+
if (card === null || card === "") return "face-down card";
|
|
219
|
+
const { rank, suit } = parseCard(card);
|
|
220
|
+
const rankName = RANK_NAMES[rank.toUpperCase()];
|
|
221
|
+
const suitName = SUIT_NAMES[suit];
|
|
222
|
+
if (!rankName || !suitName) return card;
|
|
223
|
+
return `${rankName} of ${suitName}`;
|
|
52
224
|
}
|
|
53
225
|
|
|
54
226
|
// src/seat.ts
|
|
55
227
|
function nextSeatIndex(current, seatCount, opts = {}) {
|
|
56
|
-
const
|
|
228
|
+
const floored = Math.floor(seatCount);
|
|
229
|
+
const n = Number.isFinite(floored) && floored > 0 ? floored : 1;
|
|
57
230
|
const dir = opts.dir ?? 1;
|
|
58
231
|
const skip = opts.skip;
|
|
59
|
-
const start = (Math.floor(current) % n + n) % n;
|
|
232
|
+
const start = ((Math.floor(current) || 0) % n + n) % n;
|
|
60
233
|
let i = start;
|
|
61
234
|
for (let step = 0; step < n - 1; step++) {
|
|
62
235
|
i = (i + dir + n) % n;
|
|
@@ -65,6 +238,130 @@ function nextSeatIndex(current, seatCount, opts = {}) {
|
|
|
65
238
|
return start;
|
|
66
239
|
}
|
|
67
240
|
|
|
241
|
+
// src/geometry.ts
|
|
242
|
+
function normCount(count) {
|
|
243
|
+
const n = Math.floor(count);
|
|
244
|
+
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
245
|
+
}
|
|
246
|
+
function seatAngle(index, seatCount, startAngle = 90) {
|
|
247
|
+
const n = normCount(seatCount);
|
|
248
|
+
return startAngle + index * 360 / n;
|
|
249
|
+
}
|
|
250
|
+
function ellipsePoint(cx, cy, rx, ry, angleDeg) {
|
|
251
|
+
const rad = angleDeg * Math.PI / 180;
|
|
252
|
+
return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };
|
|
253
|
+
}
|
|
254
|
+
function ringPoints(count, cx, cy, rx, ry, startAngle = 90) {
|
|
255
|
+
const n = normCount(count);
|
|
256
|
+
return Array.from({ length: n }, (_, i) => ellipsePoint(cx, cy, rx, ry, seatAngle(i, n, startAngle)));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/layout.ts
|
|
260
|
+
var DESIGN_W = 1200;
|
|
261
|
+
var DESIGN_H = 900;
|
|
262
|
+
var DEFAULT_MARGIN_X = 164 / DESIGN_W;
|
|
263
|
+
var DEFAULT_MARGIN_Y = 196 / DESIGN_H;
|
|
264
|
+
var DEFAULT_FELT_GAP = 11;
|
|
265
|
+
var DEFAULT_BET_SCALE = 0.72;
|
|
266
|
+
var DEFAULT_SEAT_GAP = 24;
|
|
267
|
+
var DEFAULT_SEAT_W = 160;
|
|
268
|
+
var DEFAULT_SEAT_H = 176;
|
|
269
|
+
var RIM_GAP = 6;
|
|
270
|
+
var GLOW_GAP = 2;
|
|
271
|
+
var SEAT_SHRINK_FLOOR = 0.5;
|
|
272
|
+
function tableRadii(designWidth, designHeight, marginX, marginY) {
|
|
273
|
+
return {
|
|
274
|
+
rx: Math.max(0, designWidth / 2 - marginX * designWidth),
|
|
275
|
+
ry: Math.max(0, designHeight / 2 - marginY * designHeight)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function seatRingRadii(designWidth, designHeight, tableRx, tableRy, seatW, seatH, feltGap) {
|
|
279
|
+
const rx = Math.min(tableRx - feltGap + seatW / 2 + 6, designWidth / 2 - seatW / 2);
|
|
280
|
+
const ry = Math.min(tableRy - feltGap + seatH / 2 + 6, designHeight / 2 - seatH / 2);
|
|
281
|
+
return { rx: Math.max(0, rx), ry: Math.max(0, ry) };
|
|
282
|
+
}
|
|
283
|
+
function seatPoint(index, count, cx, cy, ringRx, ringRy, startAngle = 90) {
|
|
284
|
+
return ellipsePoint(cx, cy, ringRx, ringRy, seatAngle(index, count, startAngle));
|
|
285
|
+
}
|
|
286
|
+
function ellipseBox(designWidth, designHeight, rx, ry) {
|
|
287
|
+
const top = designHeight / 2 - ry;
|
|
288
|
+
const left = designWidth / 2 - rx;
|
|
289
|
+
return { top, right: left, bottom: top, left };
|
|
290
|
+
}
|
|
291
|
+
function boxSeparation(a, b) {
|
|
292
|
+
const dx = Math.max(b.x - a.x - (a.width + b.width) / 2, a.x - b.x - (a.width + b.width) / 2);
|
|
293
|
+
const dy = Math.max(b.y - a.y - (a.height + b.height) / 2, a.y - b.y - (a.height + b.height) / 2);
|
|
294
|
+
if (dx > 0 && dy > 0) return Math.hypot(dx, dy);
|
|
295
|
+
if (dx > 0) return dx;
|
|
296
|
+
if (dy > 0) return dy;
|
|
297
|
+
return Math.max(dx, dy);
|
|
298
|
+
}
|
|
299
|
+
function minSeatSeparation(seats) {
|
|
300
|
+
let worst = Infinity;
|
|
301
|
+
for (let i = 0; i < seats.length; i++) {
|
|
302
|
+
for (let j = i + 1; j < seats.length; j++) {
|
|
303
|
+
const s = boxSeparation(seats[i], seats[j]);
|
|
304
|
+
if (s < worst) worst = s;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return worst;
|
|
308
|
+
}
|
|
309
|
+
function positionsFor(count, cx, cy, ringRx, ringRy, seatW, seatH, startAngle) {
|
|
310
|
+
return Array.from({ length: count }, (_, index) => {
|
|
311
|
+
const p = seatPoint(index, count, cx, cy, ringRx, ringRy, startAngle);
|
|
312
|
+
return { index, x: p.x, y: p.y, width: seatW, height: seatH };
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
function fitSeatScale(opts) {
|
|
316
|
+
if (opts.seatGap <= 0) return 1;
|
|
317
|
+
const { rx, ry } = tableRadii(opts.designWidth, opts.designHeight, opts.marginX, opts.marginY);
|
|
318
|
+
const cx = opts.designWidth / 2;
|
|
319
|
+
const cy = opts.designHeight / 2;
|
|
320
|
+
for (let step = 0; step <= 50; step++) {
|
|
321
|
+
const scale = 1 - step * 0.01;
|
|
322
|
+
const w = Math.max(1, Math.round(opts.seatWidth * scale));
|
|
323
|
+
const h = Math.max(1, Math.round(opts.seatHeight * scale));
|
|
324
|
+
const ring = seatRingRadii(opts.designWidth, opts.designHeight, rx, ry, w, h, opts.feltGap);
|
|
325
|
+
const seats = positionsFor(opts.count, cx, cy, ring.rx, ring.ry, w, h, opts.startAngle);
|
|
326
|
+
if (minSeatSeparation(seats) >= opts.seatGap) return scale;
|
|
327
|
+
if (scale <= SEAT_SHRINK_FLOOR) break;
|
|
328
|
+
}
|
|
329
|
+
return SEAT_SHRINK_FLOOR;
|
|
330
|
+
}
|
|
331
|
+
function layoutSeats(opts) {
|
|
332
|
+
const { rx, ry } = tableRadii(opts.designWidth, opts.designHeight, opts.marginX, opts.marginY);
|
|
333
|
+
const cx = opts.designWidth / 2;
|
|
334
|
+
const cy = opts.designHeight / 2;
|
|
335
|
+
const scale = fitSeatScale(opts);
|
|
336
|
+
const seatW = Math.max(1, Math.round(opts.seatWidth * scale));
|
|
337
|
+
const seatH = Math.max(1, Math.round(opts.seatHeight * scale));
|
|
338
|
+
const ring = seatRingRadii(opts.designWidth, opts.designHeight, rx, ry, seatW, seatH, opts.feltGap);
|
|
339
|
+
const betScale = Math.min(1, Math.max(0.2, opts.betRadiusScale ?? DEFAULT_BET_SCALE));
|
|
340
|
+
const betRx = rx * betScale;
|
|
341
|
+
const betRy = ry * betScale;
|
|
342
|
+
const box = (bx, by) => ellipseBox(opts.designWidth, opts.designHeight, bx, by);
|
|
343
|
+
return {
|
|
344
|
+
cx,
|
|
345
|
+
cy,
|
|
346
|
+
tableRx: rx,
|
|
347
|
+
tableRy: ry,
|
|
348
|
+
ringRx: ring.rx,
|
|
349
|
+
ringRy: ring.ry,
|
|
350
|
+
betRx,
|
|
351
|
+
betRy,
|
|
352
|
+
seatW,
|
|
353
|
+
seatH,
|
|
354
|
+
seats: positionsFor(opts.count, cx, cy, ring.rx, ring.ry, seatW, seatH, opts.startAngle),
|
|
355
|
+
boxes: {
|
|
356
|
+
table: box(rx, ry),
|
|
357
|
+
felt: box(rx - opts.feltGap, ry - opts.feltGap),
|
|
358
|
+
rim: box(rx - RIM_GAP, ry - RIM_GAP),
|
|
359
|
+
glow: box(rx - GLOW_GAP, ry - GLOW_GAP),
|
|
360
|
+
bettingLine: box(betRx, betRy)
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
68
365
|
// src/components/PlayingCard.tsx
|
|
69
366
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
70
367
|
var SIZE_CLASSES = {
|
|
@@ -105,82 +402,77 @@ var BACK_EMBLEM_SIZE = {
|
|
|
105
402
|
md: 28,
|
|
106
403
|
lg: 42
|
|
107
404
|
};
|
|
108
|
-
function parseCard(card) {
|
|
109
|
-
if (!card || card.length < 2) return { rank: "", suit: "" };
|
|
110
|
-
return { rank: card.slice(0, -1), suit: card.slice(-1) };
|
|
111
|
-
}
|
|
112
|
-
function getSuitSymbol(suit) {
|
|
113
|
-
switch (suit) {
|
|
114
|
-
case "s":
|
|
115
|
-
return "\u2660";
|
|
116
|
-
case "h":
|
|
117
|
-
return "\u2665";
|
|
118
|
-
case "d":
|
|
119
|
-
return "\u2666";
|
|
120
|
-
case "c":
|
|
121
|
-
return "\u2663";
|
|
122
|
-
default:
|
|
123
|
-
return suit;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
405
|
function getSuitClass(suit) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
case "c":
|
|
130
|
-
return "text-neutral-900";
|
|
131
|
-
case "h":
|
|
132
|
-
case "d":
|
|
133
|
-
return "text-red-600";
|
|
134
|
-
default:
|
|
135
|
-
return "";
|
|
136
|
-
}
|
|
406
|
+
if (isRedSuit(suit)) return "text-red-600";
|
|
407
|
+
return isKnownSuit(suit) ? "text-neutral-900" : "";
|
|
137
408
|
}
|
|
138
409
|
var CARD_FACE = "relative bg-gradient-to-br from-white via-white to-neutral-100 rounded-md border border-neutral-300/90 overflow-hidden shadow-[0_2px_6px_rgba(0,0,0,0.45),inset_0_1px_0_rgba(255,255,255,0.9)]";
|
|
139
|
-
function PlayingCard({ card, size = "md", className }) {
|
|
140
|
-
if (!card) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardBack, { size, className });
|
|
410
|
+
function PlayingCard({ card, size = "md", label, className }) {
|
|
411
|
+
if (!card) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CardBack, { size, className, label });
|
|
412
|
+
const name = label ?? describeCard(card);
|
|
141
413
|
const { rank, suit } = parseCard(card);
|
|
142
414
|
const suitSymbol = getSuitSymbol(suit);
|
|
143
415
|
const suitClass = getSuitClass(suit);
|
|
144
416
|
const large = size === "md" || size === "lg";
|
|
145
417
|
if (!large) {
|
|
146
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
418
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
419
|
+
"div",
|
|
420
|
+
{
|
|
421
|
+
role: "img",
|
|
422
|
+
"aria-label": name,
|
|
423
|
+
className: cn(SIZE_CLASSES[size], CARD_FACE, className),
|
|
424
|
+
style: { aspectRatio: "10/14" },
|
|
425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("absolute inset-0 flex flex-col leading-none", suitClass), children: [
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(SMALL_RANK[size], "font-bold tracking-tight"), children: rank }) }),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: SMALL_SUIT[size], children: suitSymbol }) })
|
|
428
|
+
] })
|
|
429
|
+
}
|
|
430
|
+
);
|
|
150
431
|
}
|
|
151
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
432
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
433
|
+
"div",
|
|
434
|
+
{
|
|
435
|
+
role: "img",
|
|
436
|
+
"aria-label": name,
|
|
437
|
+
className: cn(SIZE_CLASSES[size], CARD_FACE, className),
|
|
438
|
+
style: { aspectRatio: "10/14" },
|
|
439
|
+
children: [
|
|
440
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("absolute top-[3px] left-[5px] flex flex-col items-center leading-none", suitClass), children: [
|
|
441
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(CORNER_RANK[size], "font-bold tracking-tight"), children: rank }),
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(CORNER_SUIT[size], "-mt-[1px]"), children: suitSymbol })
|
|
443
|
+
] }),
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("absolute bottom-[3px] right-[5px] flex flex-col items-center leading-none rotate-180", suitClass), children: [
|
|
445
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(CORNER_RANK[size], "font-bold tracking-tight"), children: rank }),
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(CORNER_SUIT[size], "-mt-[1px]"), children: suitSymbol })
|
|
447
|
+
] }),
|
|
448
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("absolute inset-0 flex items-center justify-center opacity-90", suitClass), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: CENTER_SUIT[size], children: suitSymbol }) })
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
);
|
|
162
452
|
}
|
|
163
453
|
function CardBackEmblem({ size }) {
|
|
164
454
|
const s = BACK_EMBLEM_SIZE[size];
|
|
165
455
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: s, height: s, viewBox: "0 0 40 40", className: "drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]", "aria-hidden": "true", children: [
|
|
166
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "#f0c04a", opacity: "0.92", children: [
|
|
456
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "var(--color-card-back-emblem, #f0c04a)", opacity: "0.92", children: [
|
|
167
457
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "20", cy: "11", rx: "4", ry: "7" }),
|
|
168
458
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "20", cy: "29", rx: "4", ry: "7" }),
|
|
169
459
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "11", cy: "20", rx: "7", ry: "4" }),
|
|
170
460
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ellipse", { cx: "29", cy: "20", rx: "7", ry: "4" })
|
|
171
461
|
] }),
|
|
172
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "20", cy: "20", r: "3.2", fill: "#5a0c18" })
|
|
462
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "20", cy: "20", r: "3.2", fill: "var(--color-card-back, #5a0c18)" })
|
|
173
463
|
] });
|
|
174
464
|
}
|
|
175
|
-
function CardBack({ size = "md", className }) {
|
|
465
|
+
function CardBack({ size = "md", label = "face-down card", className }) {
|
|
176
466
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
177
467
|
"div",
|
|
178
468
|
{
|
|
469
|
+
role: "img",
|
|
470
|
+
"aria-label": label,
|
|
179
471
|
className: cn(
|
|
180
472
|
SIZE_CLASSES[size],
|
|
181
473
|
"relative rounded-md p-[3px] overflow-hidden",
|
|
182
|
-
"bg-gradient-to-br from-
|
|
183
|
-
"border-2 border-
|
|
474
|
+
"bg-gradient-to-br from-card-back-light via-card-back to-card-back-dark",
|
|
475
|
+
"border-2 border-card-back-ink/70 shadow-[0_2px_6px_rgba(0,0,0,0.45)]",
|
|
184
476
|
className
|
|
185
477
|
),
|
|
186
478
|
style: { aspectRatio: "10/14" },
|
|
@@ -188,8 +480,23 @@ function CardBack({ size = "md", className }) {
|
|
|
188
480
|
}
|
|
189
481
|
);
|
|
190
482
|
}
|
|
191
|
-
function HoleCards({ cards, size = "lg", className, deal = true }) {
|
|
192
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex gap-2", className), children: cards.map((card, i) =>
|
|
483
|
+
function HoleCards({ cards, size = "lg", className, deal = true, dealKey }) {
|
|
484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("flex gap-2", className), children: cards.map((card, i) => (
|
|
485
|
+
/*
|
|
486
|
+
* Keying on the card rather than the index is what makes the deal
|
|
487
|
+
* animation replay: React reuses an element whose key is unchanged, and
|
|
488
|
+
* a reused element never restarts its CSS animation.
|
|
489
|
+
*/
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
491
|
+
"div",
|
|
492
|
+
{
|
|
493
|
+
className: cn(deal && "animate-deal-in"),
|
|
494
|
+
style: deal ? { animationDelay: `${i * DEAL_STAGGER_MS}ms` } : void 0,
|
|
495
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlayingCard, { card, size })
|
|
496
|
+
},
|
|
497
|
+
dealKey !== void 0 ? `${dealKey}:${i}` : `${i}:${card ?? "back"}`
|
|
498
|
+
)
|
|
499
|
+
)) });
|
|
193
500
|
}
|
|
194
501
|
function CommunityCards({ cards, size = "md", className, animated = true, slots }) {
|
|
195
502
|
const count = slots ?? cards.length;
|
|
@@ -198,14 +505,171 @@ function CommunityCards({ cards, size = "md", className, animated = true, slots
|
|
|
198
505
|
"div",
|
|
199
506
|
{
|
|
200
507
|
className: cn("h-full w-full", animated && "animate-card-flip"),
|
|
201
|
-
style: animated ? { animationDelay: `${i *
|
|
508
|
+
style: animated ? { animationDelay: `${i * FLIP_STAGGER_MS}ms` } : void 0,
|
|
202
509
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlayingCard, { card, size })
|
|
203
510
|
}
|
|
204
511
|
) : null }, i)) });
|
|
205
512
|
}
|
|
206
513
|
|
|
207
|
-
// src/
|
|
514
|
+
// src/chips.ts
|
|
515
|
+
var DEFAULT_DENOMINATIONS = [
|
|
516
|
+
{ value: 100, color: "#1a1a2e", className: "fill-chip-black" },
|
|
517
|
+
{ value: 25, color: "#27ae60", className: "fill-chip-green" },
|
|
518
|
+
{ value: 10, color: "#2980b9", className: "fill-chip-blue" },
|
|
519
|
+
{ value: 5, color: "#c0392b", className: "fill-chip-red" },
|
|
520
|
+
{ value: 1, color: "#ecf0f1", className: "fill-chip-white" }
|
|
521
|
+
];
|
|
522
|
+
var SPOT_CANDIDATES = ["rgba(255,255,255,0.9)", "rgba(0,0,0,0.45)"];
|
|
523
|
+
function chipSpotColor(bodyColor) {
|
|
524
|
+
return mostVisible(SPOT_CANDIDATES, bodyColor);
|
|
525
|
+
}
|
|
526
|
+
function chipBreakdown(amount, denominations = DEFAULT_DENOMINATIONS) {
|
|
527
|
+
const usable = denominations.filter((d) => Number.isFinite(d.value) && d.value >= 1);
|
|
528
|
+
const sorted = [...usable].sort((a, b) => b.value - a.value);
|
|
529
|
+
let remaining = Math.max(0, Math.floor(Number.isFinite(amount) ? amount : 0));
|
|
530
|
+
const entries = [];
|
|
531
|
+
for (const denomination of sorted) {
|
|
532
|
+
const count = Math.floor(remaining / denomination.value);
|
|
533
|
+
if (count > 0) {
|
|
534
|
+
entries.push({ ...denomination, count });
|
|
535
|
+
remaining -= count * denomination.value;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return entries;
|
|
539
|
+
}
|
|
540
|
+
function chipsTotal(entries) {
|
|
541
|
+
return entries.reduce((sum, e) => sum + e.value * e.count, 0);
|
|
542
|
+
}
|
|
543
|
+
function chipCount(entries) {
|
|
544
|
+
return entries.reduce((sum, e) => sum + e.count, 0);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/components/Chip.tsx
|
|
548
|
+
var import_react = require("react");
|
|
208
549
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
550
|
+
var CHIP_WIDTH = { sm: 22, md: 30, lg: 42 };
|
|
551
|
+
var SPOT_ANGLES = [0, 45, 90, 135, 180, 225, 270, 315];
|
|
552
|
+
function Chip({
|
|
553
|
+
value,
|
|
554
|
+
color = "#2980b9",
|
|
555
|
+
colorClassName,
|
|
556
|
+
spotColor,
|
|
557
|
+
size = "md",
|
|
558
|
+
label,
|
|
559
|
+
className
|
|
560
|
+
}) {
|
|
561
|
+
const clipId = `chip-clip-${(0, import_react.useId)().replace(/:/g, "")}`;
|
|
562
|
+
const width = CHIP_WIDTH[size];
|
|
563
|
+
const spots = spotColor ?? chipSpotColor(color);
|
|
564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
565
|
+
"svg",
|
|
566
|
+
{
|
|
567
|
+
width,
|
|
568
|
+
height: width * 0.625,
|
|
569
|
+
viewBox: "0 0 48 30",
|
|
570
|
+
role: "img",
|
|
571
|
+
"aria-label": label ?? (value !== void 0 ? `${value} chip` : "chip"),
|
|
572
|
+
className,
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: clipId, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "15", rx: "22", ry: "13" }) }) }),
|
|
575
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "18", rx: "22", ry: "13", className: colorClassName, fill: color }),
|
|
576
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "18", rx: "22", ry: "13", fill: "rgba(0,0,0,0.32)" }),
|
|
577
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "15", rx: "22", ry: "13", className: colorClassName, fill: color }),
|
|
578
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { clipPath: `url(#${clipId})`, children: SPOT_ANGLES.map((angle) => {
|
|
579
|
+
const rad = angle * Math.PI / 180;
|
|
580
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
581
|
+
"rect",
|
|
582
|
+
{
|
|
583
|
+
x: -1.7,
|
|
584
|
+
y: -3,
|
|
585
|
+
width: 3.4,
|
|
586
|
+
height: 6,
|
|
587
|
+
rx: 1.3,
|
|
588
|
+
fill: spots,
|
|
589
|
+
transform: `translate(${24 + 19.3 * Math.cos(rad)} ${15 + 10.7 * Math.sin(rad)}) rotate(${angle})`
|
|
590
|
+
},
|
|
591
|
+
angle
|
|
592
|
+
);
|
|
593
|
+
}) }),
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "15", rx: "21", ry: "12.1", fill: "none", stroke: "rgba(255,255,255,0.3)", strokeWidth: "1.2" }),
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "15", rx: "13.4", ry: "7.9", fill: "rgba(255,255,255,0.94)" }),
|
|
596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "24", cy: "15", rx: "13.4", ry: "7.9", fill: "none", stroke: "rgba(0,0,0,0.18)", strokeWidth: "0.8" }),
|
|
597
|
+
value !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
598
|
+
"text",
|
|
599
|
+
{
|
|
600
|
+
x: "24",
|
|
601
|
+
y: "18.3",
|
|
602
|
+
textAnchor: "middle",
|
|
603
|
+
fontSize: "9.5",
|
|
604
|
+
fontWeight: "700",
|
|
605
|
+
fill: "#171717",
|
|
606
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
607
|
+
children: value
|
|
608
|
+
}
|
|
609
|
+
)
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
function ChipStack({
|
|
615
|
+
amount,
|
|
616
|
+
size = "md",
|
|
617
|
+
denominations = DEFAULT_DENOMINATIONS,
|
|
618
|
+
maxChips = 12,
|
|
619
|
+
label,
|
|
620
|
+
className
|
|
621
|
+
}) {
|
|
622
|
+
const entries = chipBreakdown(amount, denominations);
|
|
623
|
+
const total = chipCount(entries);
|
|
624
|
+
const drawn = [];
|
|
625
|
+
for (const entry of entries) {
|
|
626
|
+
for (let i = 0; i < entry.count && drawn.length < maxChips; i++) drawn.push(entry);
|
|
627
|
+
if (drawn.length >= maxChips) break;
|
|
628
|
+
}
|
|
629
|
+
const hidden = total - drawn.length;
|
|
630
|
+
const offset = Math.max(3, Math.round(CHIP_WIDTH[size] * 0.17));
|
|
631
|
+
const breakdown = entries.map((e) => `${e.count}x${e.label ?? e.value}`).join(", ");
|
|
632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
633
|
+
"div",
|
|
634
|
+
{
|
|
635
|
+
className: cn("inline-flex flex-col items-center gap-1", className),
|
|
636
|
+
role: "img",
|
|
637
|
+
"aria-label": `${amount} in ${total} ${total === 1 ? "chip" : "chips"}${breakdown ? `: ${breakdown}` : ""}`,
|
|
638
|
+
children: [
|
|
639
|
+
drawn.length > 0 && // Presentation only — the stack already carries the accessible name.
|
|
640
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "aria-hidden": "true", className: "flex flex-col items-center drop-shadow-[0_3px_4px_rgba(0,0,0,0.5)]", children: drawn.map((entry, i) => (
|
|
641
|
+
// Later chips are tucked under the ones above them, so z-index has
|
|
642
|
+
// to descend as the stack grows.
|
|
643
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "relative", style: { marginTop: i === 0 ? 0 : -offset, zIndex: drawn.length - i }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
644
|
+
Chip,
|
|
645
|
+
{
|
|
646
|
+
value: entry.label ?? entry.value,
|
|
647
|
+
color: entry.color,
|
|
648
|
+
colorClassName: entry.className,
|
|
649
|
+
spotColor: entry.spotColor,
|
|
650
|
+
size
|
|
651
|
+
}
|
|
652
|
+
) }, i)
|
|
653
|
+
)) }),
|
|
654
|
+
hidden > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "font-mono text-[10px] leading-none text-neutral-400", children: [
|
|
655
|
+
"+",
|
|
656
|
+
hidden
|
|
657
|
+
] }),
|
|
658
|
+
label !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "leading-none", children: label })
|
|
659
|
+
]
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/components/seatSize.ts
|
|
665
|
+
var import_react2 = require("react");
|
|
666
|
+
var SeatSizeContext = (0, import_react2.createContext)(null);
|
|
667
|
+
function useSeatSize() {
|
|
668
|
+
return (0, import_react2.useContext)(SeatSizeContext);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/components/Seat.tsx
|
|
672
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
209
673
|
function Seat({
|
|
210
674
|
isActive = false,
|
|
211
675
|
isWinner = false,
|
|
@@ -223,21 +687,24 @@ function Seat({
|
|
|
223
687
|
topLeft,
|
|
224
688
|
topRight,
|
|
225
689
|
topCenter,
|
|
226
|
-
width
|
|
227
|
-
height
|
|
690
|
+
width,
|
|
691
|
+
height,
|
|
228
692
|
className,
|
|
229
693
|
frameClassName,
|
|
230
694
|
frameStyle
|
|
231
695
|
}) {
|
|
232
|
-
const
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
/* @__PURE__ */ (0,
|
|
696
|
+
const laidOut = useSeatSize();
|
|
697
|
+
const frameWidth = width ?? laidOut?.width ?? DEFAULT_SEAT_W;
|
|
698
|
+
const frameHeight = height ?? laidOut?.height ?? DEFAULT_SEAT_H;
|
|
699
|
+
const activeClass = animated ? "border-gold animate-turn-glow" : "border-gold shadow-[0_0_18px_color-mix(in_oklab,var(--color-gold)_50%,transparent)]";
|
|
700
|
+
const winnerClass = animated ? "border-gold animate-winner-glow" : "border-gold shadow-[0_0_0_3px_color-mix(in_oklab,var(--color-gold)_90%,transparent)]";
|
|
701
|
+
const effTopLeft = topLeft !== void 0 ? topLeft : isWinner && winnerCrown ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-3xl drop-shadow-[0_2px_4px_rgba(0,0,0,0.6)] animate-bounce", children: "\u{1F451}" }) : void 0;
|
|
702
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn("relative flex flex-col items-center", className), style: { width: frameWidth }, children: [
|
|
703
|
+
topCenter !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute -top-2 left-1/2 z-20 flex -translate-x-1/2 gap-1", children: topCenter }),
|
|
704
|
+
effTopLeft !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute -top-3 -left-2 z-40", children: effTopLeft }),
|
|
705
|
+
topRight !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute -top-2 -right-2 z-40", children: topRight }),
|
|
706
|
+
above !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pointer-events-none absolute -top-12 left-1/2 z-30 -translate-x-1/2", children: above }),
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
241
708
|
"div",
|
|
242
709
|
{
|
|
243
710
|
role: onClick ? "button" : void 0,
|
|
@@ -246,196 +713,229 @@ function Seat({
|
|
|
246
713
|
onKeyDown: onClick ? (e) => {
|
|
247
714
|
if (e.key === "Enter" || e.key === " ") onClick();
|
|
248
715
|
} : void 0,
|
|
249
|
-
style: { width, height, ...frameStyle },
|
|
716
|
+
style: { width: frameWidth, height: frameHeight, ...frameStyle },
|
|
250
717
|
className: cn(
|
|
251
718
|
"relative flex flex-col items-center justify-between gap-1 rounded-2xl border bg-neutral-900/80 p-2 text-neutral-100 transition",
|
|
252
719
|
"border-neutral-700",
|
|
253
720
|
isActive && activeClass,
|
|
254
721
|
isWinner && winnerClass,
|
|
255
722
|
isDimmed && "opacity-50",
|
|
256
|
-
onClick && "cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
723
|
+
onClick && "cursor-pointer hover:border-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-gold",
|
|
257
724
|
frameClassName
|
|
258
725
|
),
|
|
259
726
|
children: [
|
|
260
|
-
header !== void 0 && /* @__PURE__ */ (0,
|
|
261
|
-
/* @__PURE__ */ (0,
|
|
262
|
-
footer !== void 0 && /* @__PURE__ */ (0,
|
|
263
|
-
overlay !== void 0 && /* @__PURE__ */ (0,
|
|
727
|
+
header !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex w-full shrink-0 items-center justify-between gap-1 text-[11px]", children: header }),
|
|
728
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex min-h-0 flex-1 items-center justify-center", children }),
|
|
729
|
+
footer !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex w-full shrink-0 items-center justify-between text-[11px] text-neutral-300", children: footer }),
|
|
730
|
+
overlay !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pointer-events-none absolute inset-x-0 top-1/2 -translate-y-1/2 text-center", children: overlay })
|
|
264
731
|
]
|
|
265
732
|
}
|
|
266
733
|
),
|
|
267
|
-
badge !== void 0 && /* @__PURE__ */ (0,
|
|
268
|
-
below !== void 0 && /* @__PURE__ */ (0,
|
|
734
|
+
badge !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute top-0 right-0 z-40", children: badge }),
|
|
735
|
+
below !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-1 flex flex-col items-center gap-1", children: below })
|
|
269
736
|
] });
|
|
270
737
|
}
|
|
271
738
|
|
|
272
739
|
// src/components/Pot.tsx
|
|
273
|
-
var
|
|
274
|
-
var
|
|
740
|
+
var import_react3 = require("react");
|
|
741
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
742
|
+
var defaultFormat = (amount) => amount.toLocaleString();
|
|
743
|
+
function prefersReducedMotion() {
|
|
744
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
745
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
746
|
+
}
|
|
275
747
|
function CountUp({ value, format }) {
|
|
276
|
-
const [shown, setShown] = (0,
|
|
277
|
-
const
|
|
278
|
-
(0,
|
|
279
|
-
const from =
|
|
748
|
+
const [shown, setShown] = (0, import_react3.useState)(value);
|
|
749
|
+
const shownRef = (0, import_react3.useRef)(value);
|
|
750
|
+
(0, import_react3.useEffect)(() => {
|
|
751
|
+
const from = shownRef.current;
|
|
280
752
|
const to = value;
|
|
281
753
|
if (from === to) return;
|
|
754
|
+
if (prefersReducedMotion()) {
|
|
755
|
+
shownRef.current = to;
|
|
756
|
+
setShown(to);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
282
759
|
let raf = 0;
|
|
283
|
-
const
|
|
284
|
-
|
|
760
|
+
const dur = COUNT_UP_MS;
|
|
761
|
+
let origin = null;
|
|
285
762
|
const tick = (now) => {
|
|
286
|
-
|
|
763
|
+
if (origin === null) origin = now;
|
|
764
|
+
const t = Math.min(1, Math.max(0, (now - origin) / dur));
|
|
287
765
|
const eased = 1 - Math.pow(1 - t, 3);
|
|
288
|
-
|
|
766
|
+
const next = Math.round(from + (to - from) * eased);
|
|
767
|
+
shownRef.current = next;
|
|
768
|
+
setShown(next);
|
|
289
769
|
if (t < 1) raf = requestAnimationFrame(tick);
|
|
290
|
-
else fromRef.current = to;
|
|
291
770
|
};
|
|
292
771
|
raf = requestAnimationFrame(tick);
|
|
293
772
|
return () => cancelAnimationFrame(raf);
|
|
294
773
|
}, [value]);
|
|
295
|
-
return /* @__PURE__ */ (0,
|
|
774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: format(shown) });
|
|
296
775
|
}
|
|
297
|
-
function Pot({ amount, label, format =
|
|
298
|
-
return /* @__PURE__ */ (0,
|
|
299
|
-
label && /* @__PURE__ */ (0,
|
|
300
|
-
/* @__PURE__ */ (0,
|
|
776
|
+
function Pot({ amount, label, format = defaultFormat, animate = true, className }) {
|
|
777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("flex items-center gap-2 font-mono text-sm text-gold", className), children: [
|
|
778
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-neutral-400", children: label }),
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "whitespace-nowrap rounded-full border border-gold/50 bg-neutral-900/80 px-3 py-1 shadow-inner", children: animate ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CountUp, { value: amount, format }) : format(amount) })
|
|
301
780
|
] });
|
|
302
781
|
}
|
|
303
782
|
|
|
304
783
|
// src/components/CardTable.tsx
|
|
305
|
-
var
|
|
306
|
-
var
|
|
307
|
-
var
|
|
308
|
-
var
|
|
309
|
-
var
|
|
310
|
-
var MARGIN_Y = 196;
|
|
311
|
-
var TABLE_RX = DESIGN_W / 2 - MARGIN_X;
|
|
312
|
-
var TABLE_RY = DESIGN_H / 2 - MARGIN_Y;
|
|
313
|
-
var FELT_GAP = 11;
|
|
314
|
-
var BET_SCALE = 0.72;
|
|
315
|
-
var BET_RX = TABLE_RX * BET_SCALE;
|
|
316
|
-
var BET_RY = TABLE_RY * BET_SCALE;
|
|
317
|
-
var CONFETTI_COLORS = ["#f39c12", "#e74c3c", "#2ecc71", "#3498db", "#f1c40f", "#9b59b6"];
|
|
318
|
-
function getEllipsePoint(cx, cy, rx, ry, angleDeg) {
|
|
319
|
-
const rad = angleDeg * Math.PI / 180;
|
|
320
|
-
return { x: cx + rx * Math.cos(rad), y: cy + ry * Math.sin(rad) };
|
|
321
|
-
}
|
|
322
|
-
function calculateSeatLayouts(seatCount, yourSeatIndex, seatWidth, seatHeight) {
|
|
323
|
-
const cx = DESIGN_W / 2;
|
|
324
|
-
const cy = DESIGN_H / 2;
|
|
325
|
-
const ringX = TABLE_RX - FELT_GAP + seatWidth / 2 + 6;
|
|
326
|
-
const ringY = TABLE_RY - FELT_GAP + seatHeight / 2 + 6;
|
|
327
|
-
const step = 360 / seatCount;
|
|
328
|
-
const angleOf = (i) => -90 + i * step;
|
|
329
|
-
const rotationOffset = yourSeatIndex !== null ? 90 - angleOf(yourSeatIndex) : 0;
|
|
330
|
-
const layouts = [];
|
|
331
|
-
for (let i = 0; i < seatCount; i++) {
|
|
332
|
-
const pt = getEllipsePoint(cx, cy, ringX, ringY, angleOf(i) + rotationOffset);
|
|
333
|
-
layouts.push({
|
|
334
|
-
x: pt.x - seatWidth / 2,
|
|
335
|
-
y: pt.y - seatHeight / 2,
|
|
336
|
-
index: i,
|
|
337
|
-
isYou: i === yourSeatIndex
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
return layouts;
|
|
341
|
-
}
|
|
784
|
+
var import_react4 = require("react");
|
|
785
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
786
|
+
var MIN_FIT_ASPECT = 0.5;
|
|
787
|
+
var MAX_FIT_ASPECT = 2.5;
|
|
788
|
+
var DEFAULT_CONFETTI_COLORS = ["#f39c12", "#e74c3c", "#2ecc71", "#3498db", "#f1c40f", "#9b59b6"];
|
|
342
789
|
function CardTable({
|
|
343
790
|
seatCount = 6,
|
|
344
791
|
seats,
|
|
345
792
|
renderSeat,
|
|
793
|
+
bets,
|
|
794
|
+
renderBet,
|
|
795
|
+
betRadiusScale = DEFAULT_BET_SCALE,
|
|
796
|
+
startAngle = 90,
|
|
346
797
|
yourSeatIndex = null,
|
|
347
798
|
center,
|
|
348
799
|
ambient = true,
|
|
349
800
|
yourTurn = false,
|
|
350
801
|
winnerSeats = [],
|
|
351
802
|
confetti = true,
|
|
352
|
-
|
|
353
|
-
|
|
803
|
+
confettiColors = DEFAULT_CONFETTI_COLORS,
|
|
804
|
+
seatWidth = DEFAULT_SEAT_W,
|
|
805
|
+
seatHeight = DEFAULT_SEAT_H,
|
|
806
|
+
seatGap = DEFAULT_SEAT_GAP,
|
|
807
|
+
designWidth = DESIGN_W,
|
|
808
|
+
designHeight = DESIGN_H,
|
|
809
|
+
marginX = DEFAULT_MARGIN_X,
|
|
810
|
+
marginY = DEFAULT_MARGIN_Y,
|
|
811
|
+
feltGap = DEFAULT_FELT_GAP,
|
|
812
|
+
fit = "design",
|
|
354
813
|
className
|
|
355
814
|
}) {
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
[count, yourSeatIndex, seatWidth, seatHeight]
|
|
362
|
-
);
|
|
363
|
-
(0, import_react2.useLayoutEffect)(() => {
|
|
815
|
+
const requested = Math.floor(seatCount);
|
|
816
|
+
const count = Number.isFinite(requested) ? Math.max(1, requested) : 1;
|
|
817
|
+
const outerRef = (0, import_react4.useRef)(null);
|
|
818
|
+
const [box, setBox] = (0, import_react4.useState)({ w: 0, h: 0 });
|
|
819
|
+
(0, import_react4.useLayoutEffect)(() => {
|
|
364
820
|
const el = outerRef.current;
|
|
365
821
|
if (!el) return;
|
|
366
822
|
const compute = () => {
|
|
367
823
|
const w = el.clientWidth;
|
|
368
824
|
const h = el.clientHeight;
|
|
369
825
|
if (!w || !h) return;
|
|
370
|
-
|
|
826
|
+
setBox({ w, h });
|
|
371
827
|
};
|
|
372
828
|
compute();
|
|
373
829
|
const ro = new ResizeObserver(compute);
|
|
374
830
|
ro.observe(el);
|
|
375
831
|
return () => ro.disconnect();
|
|
376
832
|
}, []);
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
833
|
+
const geo = (0, import_react4.useMemo)(() => {
|
|
834
|
+
const dh = designHeight;
|
|
835
|
+
const dw = fit === "container" && box.w > 0 && box.h > 0 ? dh * Math.min(MAX_FIT_ASPECT, Math.max(MIN_FIT_ASPECT, box.w / box.h)) : designWidth;
|
|
836
|
+
const layout2 = layoutSeats({
|
|
837
|
+
count,
|
|
838
|
+
designWidth: dw,
|
|
839
|
+
designHeight: dh,
|
|
840
|
+
marginX,
|
|
841
|
+
marginY,
|
|
842
|
+
feltGap,
|
|
843
|
+
seatWidth,
|
|
844
|
+
seatHeight,
|
|
845
|
+
// Always applied. Every Seat inside the table is told its frame size
|
|
846
|
+
// through context, so the shrink is no longer limited to `renderSeat`.
|
|
847
|
+
seatGap,
|
|
848
|
+
startAngle,
|
|
849
|
+
betRadiusScale
|
|
850
|
+
});
|
|
851
|
+
const scale2 = box.w > 0 && box.h > 0 ? Math.min(box.w / dw, box.h / dh) : 1;
|
|
852
|
+
return { designW: dw, designH: dh, scale: scale2, layout: layout2 };
|
|
853
|
+
}, [
|
|
854
|
+
count,
|
|
855
|
+
designWidth,
|
|
856
|
+
designHeight,
|
|
857
|
+
marginX,
|
|
858
|
+
marginY,
|
|
859
|
+
feltGap,
|
|
860
|
+
seatWidth,
|
|
861
|
+
seatHeight,
|
|
862
|
+
seatGap,
|
|
863
|
+
startAngle,
|
|
864
|
+
fit,
|
|
865
|
+
betRadiusScale,
|
|
866
|
+
box.w,
|
|
867
|
+
box.h
|
|
868
|
+
]);
|
|
869
|
+
const { designW, designH, scale, layout } = geo;
|
|
870
|
+
const { cx, cy, seatW, seatH, betRx, betRy, boxes } = layout;
|
|
871
|
+
const hasBets = bets !== void 0 || renderBet !== void 0;
|
|
872
|
+
const px = (b) => ({
|
|
873
|
+
top: `${b.top}px`,
|
|
874
|
+
right: `${b.right}px`,
|
|
875
|
+
bottom: `${b.bottom}px`,
|
|
876
|
+
left: `${b.left}px`
|
|
877
|
+
});
|
|
878
|
+
const tableFrameInset = px(boxes.table);
|
|
879
|
+
const feltInset = px(boxes.felt);
|
|
880
|
+
const rimInset = px(boxes.rim);
|
|
881
|
+
const ringInset = px(boxes.glow);
|
|
882
|
+
const betLineInset = px(boxes.bettingLine);
|
|
883
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
385
884
|
"div",
|
|
386
885
|
{
|
|
387
886
|
ref: outerRef,
|
|
388
887
|
className: cn("relative flex h-full w-full min-h-0 items-center justify-center overflow-hidden", className),
|
|
389
|
-
children: /* @__PURE__ */ (0,
|
|
888
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { width: designW * scale, height: designH * scale }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
390
889
|
"div",
|
|
391
890
|
{
|
|
392
891
|
className: "relative",
|
|
393
|
-
style: { width:
|
|
892
|
+
style: { width: designW, height: designH, transform: `scale(${scale})`, transformOrigin: "top left" },
|
|
394
893
|
children: [
|
|
395
|
-
/* @__PURE__ */ (0,
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
396
895
|
"div",
|
|
397
896
|
{
|
|
398
|
-
className: "absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,
|
|
897
|
+
className: "absolute -inset-6 rounded-[50%] bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--color-gold-light)_16%,transparent),transparent_72%)] blur-3xl",
|
|
399
898
|
"aria-hidden": "true"
|
|
400
899
|
}
|
|
401
900
|
),
|
|
402
|
-
/* @__PURE__ */ (0,
|
|
901
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
403
902
|
"div",
|
|
404
903
|
{
|
|
405
|
-
className: "absolute rounded-[50%] bg-gradient-to-b from-
|
|
904
|
+
className: "absolute rounded-[50%] bg-gradient-to-b from-rail via-rail-dark to-rail-deep shadow-[0_26px_70px_-12px_rgba(0,0,0,0.85),inset_0_3px_0_rgba(255,255,255,0.20),inset_0_-6px_16px_rgba(0,0,0,0.5)]",
|
|
406
905
|
style: tableFrameInset,
|
|
407
906
|
"aria-hidden": "true"
|
|
408
907
|
}
|
|
409
908
|
),
|
|
410
|
-
/* @__PURE__ */ (0,
|
|
909
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
411
910
|
"div",
|
|
412
911
|
{
|
|
413
|
-
className: "absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32
|
|
912
|
+
className: "absolute rounded-[50%] bg-[radial-gradient(ellipse_at_50%_32%,var(--color-felt-light),var(--color-felt)_55%,var(--color-felt-dark))] shadow-[inset_0_0_90px_rgba(0,0,0,0.55),inset_0_3px_10px_rgba(255,255,255,0.06)]",
|
|
414
913
|
style: feltInset,
|
|
415
914
|
"aria-hidden": "true"
|
|
416
915
|
}
|
|
417
916
|
),
|
|
418
|
-
ambient && /* @__PURE__ */ (0,
|
|
419
|
-
/* @__PURE__ */ (0,
|
|
917
|
+
ambient && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
918
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
420
919
|
"div",
|
|
421
920
|
{
|
|
422
|
-
className: "absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,
|
|
921
|
+
className: "absolute animate-spotlight rounded-[50%] bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--color-gold-glow)_14%,transparent),transparent_70%)] blur-2xl",
|
|
423
922
|
style: feltInset,
|
|
424
923
|
"aria-hidden": "true"
|
|
425
924
|
}
|
|
426
925
|
),
|
|
427
|
-
/* @__PURE__ */ (0,
|
|
926
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute overflow-hidden rounded-[50%]", style: feltInset, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute inset-0 animate-felt-shimmer bg-[linear-gradient(115deg,transparent_42%,rgba(255,255,255,0.06)_50%,transparent_58%)] bg-[length:220%_100%]" }) })
|
|
428
927
|
] }),
|
|
429
|
-
/* @__PURE__ */ (0,
|
|
928
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
430
929
|
"div",
|
|
431
930
|
{
|
|
931
|
+
"data-part": "betting-line",
|
|
432
932
|
className: "absolute rounded-[50%] border-2 border-white/10 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.25)]",
|
|
433
933
|
style: betLineInset,
|
|
434
934
|
"aria-hidden": "true"
|
|
435
935
|
}
|
|
436
936
|
),
|
|
437
|
-
/* @__PURE__ */ (0,
|
|
438
|
-
yourTurn && /* @__PURE__ */ (0,
|
|
937
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute flex items-center justify-center pointer-events-none", style: feltInset, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-white/[0.05] text-[110px] font-black tracking-[0.2em] select-none leading-none", children: "\u2660\u2665\u2666\u2663" }) }),
|
|
938
|
+
yourTurn && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
439
939
|
"div",
|
|
440
940
|
{
|
|
441
941
|
className: "pointer-events-none absolute z-10 animate-turn-glow rounded-[50%]",
|
|
@@ -443,18 +943,46 @@ function CardTable({
|
|
|
443
943
|
"aria-hidden": "true"
|
|
444
944
|
}
|
|
445
945
|
),
|
|
446
|
-
center !== void 0 && /* @__PURE__ */ (0,
|
|
447
|
-
/* @__PURE__ */ (0,
|
|
448
|
-
const
|
|
946
|
+
center !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center", children: center }),
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute inset-0 z-20", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SeatSizeContext.Provider, { value: { width: seatW, height: seatH }, children: layout.seats.map((seat) => {
|
|
948
|
+
const { index, x, y } = seat;
|
|
949
|
+
const isYou = index === yourSeatIndex;
|
|
950
|
+
const box2 = { index, isYou, x, y, width: seatW, height: seatH };
|
|
951
|
+
const content = seats ? seats[index] ?? null : renderSeat ? renderSeat(index, isYou, box2) : null;
|
|
449
952
|
if (content === null) return null;
|
|
450
|
-
return /* @__PURE__ */ (0,
|
|
953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
954
|
+
"div",
|
|
955
|
+
{
|
|
956
|
+
"data-part": "seat",
|
|
957
|
+
"data-seat": index,
|
|
958
|
+
className: "absolute",
|
|
959
|
+
style: { left: `${x - seatW / 2}px`, top: `${y - seatH / 2}px` },
|
|
960
|
+
children: content
|
|
961
|
+
},
|
|
962
|
+
index
|
|
963
|
+
);
|
|
964
|
+
}) }) }),
|
|
965
|
+
hasBets && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pointer-events-none absolute inset-0 z-20", children: layout.seats.map(({ index }) => {
|
|
966
|
+
const content = bets ? bets[index] ?? null : renderBet ? renderBet(index) : null;
|
|
967
|
+
if (content === null || content === void 0) return null;
|
|
968
|
+
const pt = ellipsePoint(cx, cy, betRx, betRy, seatAngle(index, count, startAngle));
|
|
969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
970
|
+
"div",
|
|
971
|
+
{
|
|
972
|
+
"data-seat-bet": index,
|
|
973
|
+
className: "absolute",
|
|
974
|
+
style: { left: `${pt.x}px`, top: `${pt.y}px`, transform: "translate(-50%, -50%)" },
|
|
975
|
+
children: content
|
|
976
|
+
},
|
|
977
|
+
index
|
|
978
|
+
);
|
|
451
979
|
}) }),
|
|
452
|
-
confetti && winnerSeats.length > 0 && /* @__PURE__ */ (0,
|
|
980
|
+
confetti && winnerSeats.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "pointer-events-none absolute inset-0 z-30 overflow-hidden", "aria-hidden": "true", children: Array.from({ length: 44 }).map((_, i) => {
|
|
453
981
|
const w = 5 + i % 4 * 2;
|
|
454
982
|
const h = 9 + i % 3 * 4;
|
|
455
983
|
const dur = 1.7 + i % 5 * 0.28;
|
|
456
984
|
const delay = i % 12 * 0.14;
|
|
457
|
-
return /* @__PURE__ */ (0,
|
|
985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
458
986
|
"span",
|
|
459
987
|
{
|
|
460
988
|
className: "absolute top-[26%] animate-confetti rounded-[2px]",
|
|
@@ -462,7 +990,7 @@ function CardTable({
|
|
|
462
990
|
left: `${i * 37 % 96}%`,
|
|
463
991
|
width: w,
|
|
464
992
|
height: h,
|
|
465
|
-
backgroundColor:
|
|
993
|
+
backgroundColor: confettiColors[i % confettiColors.length],
|
|
466
994
|
animationDuration: `${dur}s`,
|
|
467
995
|
animationDelay: `${delay}s`,
|
|
468
996
|
animationIterationCount: "infinite"
|
|
@@ -471,7 +999,7 @@ function CardTable({
|
|
|
471
999
|
i
|
|
472
1000
|
);
|
|
473
1001
|
}) }),
|
|
474
|
-
/* @__PURE__ */ (0,
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute rounded-[50%] border border-white/10 pointer-events-none", style: rimInset, "aria-hidden": "true" })
|
|
475
1003
|
]
|
|
476
1004
|
}
|
|
477
1005
|
) })
|
|
@@ -481,18 +1009,61 @@ function CardTable({
|
|
|
481
1009
|
// Annotate the CommonJS export names for ESM import in node:
|
|
482
1010
|
0 && (module.exports = {
|
|
483
1011
|
BB,
|
|
1012
|
+
COUNT_UP_MS,
|
|
484
1013
|
CardBack,
|
|
485
1014
|
CardTable,
|
|
1015
|
+
Chip,
|
|
1016
|
+
ChipStack,
|
|
486
1017
|
CommunityCards,
|
|
1018
|
+
DEAL_STAGGER_MS,
|
|
1019
|
+
DEFAULT_BET_SCALE,
|
|
1020
|
+
DEFAULT_DENOMINATIONS,
|
|
1021
|
+
DEFAULT_FELT_GAP,
|
|
1022
|
+
DEFAULT_MARGIN_X,
|
|
1023
|
+
DEFAULT_MARGIN_Y,
|
|
1024
|
+
DEFAULT_SEAT_GAP,
|
|
1025
|
+
DEFAULT_SEAT_H,
|
|
1026
|
+
DEFAULT_SEAT_W,
|
|
1027
|
+
DESIGN_H,
|
|
1028
|
+
DESIGN_W,
|
|
1029
|
+
FLIP_STAGGER_MS,
|
|
1030
|
+
GLOW_GAP,
|
|
487
1031
|
HoleCards,
|
|
488
1032
|
PlayingCard,
|
|
489
1033
|
PokerTable,
|
|
490
1034
|
Pot,
|
|
1035
|
+
RIM_GAP,
|
|
1036
|
+
SEAT_SHRINK_FLOOR,
|
|
1037
|
+
SIZES,
|
|
491
1038
|
Seat,
|
|
1039
|
+
chipBreakdown,
|
|
1040
|
+
chipCount,
|
|
1041
|
+
chipSpotColor,
|
|
1042
|
+
chipsTotal,
|
|
492
1043
|
cn,
|
|
1044
|
+
contrastRatio,
|
|
1045
|
+
describeCard,
|
|
1046
|
+
ellipseBox,
|
|
1047
|
+
ellipsePoint,
|
|
1048
|
+
fitSeatScale,
|
|
1049
|
+
flatten,
|
|
493
1050
|
formatBB,
|
|
1051
|
+
getRankName,
|
|
1052
|
+
getSuitName,
|
|
494
1053
|
getSuitSymbol,
|
|
1054
|
+
isKnownSuit,
|
|
1055
|
+
isRedSuit,
|
|
1056
|
+
layoutSeats,
|
|
1057
|
+
minSeatSeparation,
|
|
1058
|
+
mostVisible,
|
|
495
1059
|
nextSeatIndex,
|
|
496
|
-
parseCard
|
|
1060
|
+
parseCard,
|
|
1061
|
+
parseColor,
|
|
1062
|
+
relativeLuminance,
|
|
1063
|
+
ringPoints,
|
|
1064
|
+
seatAngle,
|
|
1065
|
+
seatPoint,
|
|
1066
|
+
seatRingRadii,
|
|
1067
|
+
tableRadii
|
|
497
1068
|
});
|
|
498
1069
|
//# sourceMappingURL=index.cjs.map
|