@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.d.cts
CHANGED
|
@@ -4,10 +4,140 @@ import { ReactNode, CSSProperties } from 'react';
|
|
|
4
4
|
/** Join class names and resolve conflicting Tailwind utilities (last wins). */
|
|
5
5
|
declare function cn(...parts: Array<string | false | null | undefined>): string;
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Default big blind that `formatBB` uses when you do not pass one.
|
|
9
|
+
*
|
|
10
|
+
* Kept for back-compat. Nothing in the package applies it on your behalf: the
|
|
11
|
+
* components render plain numbers unless you hand them a formatter, because a
|
|
12
|
+
* big blind is a poker rule and this is not a poker library.
|
|
13
|
+
*/
|
|
8
14
|
declare const BB = 20;
|
|
9
|
-
/**
|
|
10
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Format an amount as big blinds, e.g. 1234 -> "61.7 BB".
|
|
17
|
+
*
|
|
18
|
+
* This is a poker *helper*, not a default. Pass your own big blind — a
|
|
19
|
+
* fixed 20 is wrong for most tables:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* <Pot amount={1234} format={(n) => formatBB(n, 100)} />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function formatBB(amount: number, bb?: number): string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Colour maths. Nothing here knows about cards, chips or games — this is the
|
|
29
|
+
* generic layer that contrast decisions are built on.
|
|
30
|
+
*
|
|
31
|
+
* It exists because the package needs to answer "would this be visible on top of
|
|
32
|
+
* that?" in more than one place (edge spots on a chip, and anything else that
|
|
33
|
+
* grows a light/dark ink choice later), and that question should not live inside
|
|
34
|
+
* a component or a game-specific module.
|
|
35
|
+
*/
|
|
36
|
+
interface Rgba {
|
|
37
|
+
r: number;
|
|
38
|
+
g: number;
|
|
39
|
+
b: number;
|
|
40
|
+
/** 0–1. */
|
|
41
|
+
a: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Parse `#rgb`, `#rrggbb`, `rgb()` or `rgba()` into channels.
|
|
45
|
+
*
|
|
46
|
+
* Returns `null` for anything else — a `var(...)`, a named colour, a value with
|
|
47
|
+
* a color-mix() in it. Callers are expected to have a sensible fallback rather
|
|
48
|
+
* than guess, which is why this does not throw.
|
|
49
|
+
*/
|
|
50
|
+
declare function parseColor(color: string): Rgba | null;
|
|
51
|
+
/** WCAG relative luminance (0 = black, 1 = white). `null` if unparseable. */
|
|
52
|
+
declare function relativeLuminance(color: string): number | null;
|
|
53
|
+
/** Composite a possibly translucent colour over an opaque one. */
|
|
54
|
+
declare function flatten(foreground: string, background: string): Rgba | null;
|
|
55
|
+
/**
|
|
56
|
+
* WCAG contrast ratio between two colours, 1–21. `foreground` may be
|
|
57
|
+
* translucent, in which case it is composited over `background` first — which is
|
|
58
|
+
* the question you actually have when you are choosing an ink colour.
|
|
59
|
+
*
|
|
60
|
+
* `null` if either colour is unparseable.
|
|
61
|
+
*/
|
|
62
|
+
declare function contrastRatio(foreground: string, background: string): number | null;
|
|
63
|
+
/**
|
|
64
|
+
* Pick whichever candidate is more visible on `background`.
|
|
65
|
+
*
|
|
66
|
+
* Derived from measured contrast rather than a hand-tuned luminance threshold,
|
|
67
|
+
* so a mid-tone background gets the genuinely better of the two instead of
|
|
68
|
+
* whichever side of an arbitrary cut-off it fell on. Falls back to the first
|
|
69
|
+
* candidate when the background cannot be measured.
|
|
70
|
+
*/
|
|
71
|
+
declare function mostVisible(candidates: readonly string[], background: string): string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The size vocabulary, in one place.
|
|
75
|
+
*
|
|
76
|
+
* Cards and chips keep their own pixel tables — a `sm` card and a `sm` chip are
|
|
77
|
+
* different objects and pretending otherwise would be worse — but they share one
|
|
78
|
+
* list of names. That way adding an `xl` to the vocabulary is a single edit and
|
|
79
|
+
* a component cannot silently support a size the type does not mention.
|
|
80
|
+
*/
|
|
81
|
+
declare const SIZES: readonly ["xs", "sm", "md", "lg"];
|
|
82
|
+
/** Any size in the vocabulary. */
|
|
83
|
+
type Size = (typeof SIZES)[number];
|
|
84
|
+
/** `PlayingCard` / `HoleCards` / `CommunityCards`: the full range. */
|
|
85
|
+
type CardSize = Size;
|
|
86
|
+
/** `Chip` / `ChipStack`: a chip has no `xs`. */
|
|
87
|
+
type ChipSize = Exclude<Size, "xs">;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Animation timing, in one place.
|
|
91
|
+
*
|
|
92
|
+
* These were magic numbers inside three components. They are kept short on
|
|
93
|
+
* purpose: the on-by-default animations are game feel, and a consumer who wants
|
|
94
|
+
* a different tempo turns them off rather than tuning them.
|
|
95
|
+
*/
|
|
96
|
+
/** How long the pot takes to count from one value to the next, in ms. */
|
|
97
|
+
declare const COUNT_UP_MS = 500;
|
|
98
|
+
/** Stagger between hole cards dealing in, in ms. */
|
|
99
|
+
declare const DEAL_STAGGER_MS = 100;
|
|
100
|
+
/** Stagger between community cards flipping in, in ms. */
|
|
101
|
+
declare const FLIP_STAGGER_MS = 70;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Card codes and how to say them out loud. Pure functions, no React.
|
|
105
|
+
*
|
|
106
|
+
* This lives outside the component because two things need it: the artwork
|
|
107
|
+
* (`PlayingCard` / `CardBack`, for the accessible name) and any consumer
|
|
108
|
+
* building a hand list, a log line or a replay. A single vocabulary is what
|
|
109
|
+
* stops every call site inventing its own way of saying "As".
|
|
110
|
+
*/
|
|
111
|
+
interface ParsedCard {
|
|
112
|
+
rank: string;
|
|
113
|
+
suit: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Split a compact card code like `"As"` or `"10h"` into rank and suit.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately permissive: it takes the last character as the suit and trusts
|
|
119
|
+
* the rest, so `"xyz"` parses as rank `"xy"` / suit `"z"`. Validation is a known
|
|
120
|
+
* gap — use `describeCard`, which falls back to the raw code rather than
|
|
121
|
+
* pretending to know what it read.
|
|
122
|
+
*/
|
|
123
|
+
declare function parseCard(card: string): ParsedCard;
|
|
124
|
+
/** The glyph for a suit letter. Unknown suits are passed through unchanged. */
|
|
125
|
+
declare function getSuitSymbol(suit: string): string;
|
|
126
|
+
/** `"A"` → `"Ace"`, `"10"` → `"Ten"`. Unknown ranks pass through. */
|
|
127
|
+
declare function getRankName(rank: string): string;
|
|
128
|
+
/** `"s"` → `"spades"`. Unknown suits pass through. */
|
|
129
|
+
declare function getSuitName(suit: string): string;
|
|
130
|
+
/** Hearts and diamonds are the red ones. */
|
|
131
|
+
declare function isRedSuit(suit: string): boolean;
|
|
132
|
+
/** Whether a suit letter is one this package knows how to draw. */
|
|
133
|
+
declare function isKnownSuit(suit: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* A spoken description: `"As"` → `"Ace of spades"`, `null` → `"face-down card"`.
|
|
136
|
+
*
|
|
137
|
+
* Returns the raw code when the rank or suit is not recognised, rather than
|
|
138
|
+
* announcing something the card is not.
|
|
139
|
+
*/
|
|
140
|
+
declare function describeCard(card: string | null): string;
|
|
11
141
|
|
|
12
142
|
/**
|
|
13
143
|
* Seat-ring helpers. Pure functions — they contain no game-specific rules.
|
|
@@ -25,18 +155,194 @@ interface NextSeatOptions {
|
|
|
25
155
|
*/
|
|
26
156
|
declare function nextSeatIndex(current: number, seatCount: number, opts?: NextSeatOptions): number;
|
|
27
157
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Pure ellipse geometry for the card table. No game rules, no React.
|
|
160
|
+
*
|
|
161
|
+
* Everything is derived from one parametric ellipse:
|
|
162
|
+
*
|
|
163
|
+
* x(θ) = cx + rx·cos θ
|
|
164
|
+
* y(θ) = cy + ry·sin θ
|
|
165
|
+
*
|
|
166
|
+
* with θ in degrees. Screen coordinates (y grows downward), so θ = 90° is the
|
|
167
|
+
* point directly BELOW the centre.
|
|
168
|
+
*
|
|
169
|
+
* Seat 0 sits at `startAngle` (default 90° = directly below the centre) and
|
|
170
|
+
* every following seat advances by `360 / seatCount`, so any count from 2 to 10
|
|
171
|
+
* spreads evenly. The betting line uses the same formula with a smaller radius.
|
|
172
|
+
*/
|
|
173
|
+
interface Point {
|
|
174
|
+
x: number;
|
|
175
|
+
y: number;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Ring angle (degrees) of seat `index`, evenly divided around 360°.
|
|
179
|
+
* Seat 0 is at `startAngle` (default 90° = directly below the table centre).
|
|
180
|
+
*/
|
|
181
|
+
declare function seatAngle(index: number, seatCount: number, startAngle?: number): number;
|
|
182
|
+
/** Point on the ellipse at `angleDeg` (degrees; 90° = below the centre). */
|
|
183
|
+
declare function ellipsePoint(cx: number, cy: number, rx: number, ry: number, angleDeg: number): Point;
|
|
184
|
+
/**
|
|
185
|
+
* `count` evenly spaced points on an ellipse, starting at `startAngle`
|
|
186
|
+
* (default 90° = directly below the centre). Handy for seat rings and betting
|
|
187
|
+
* lines alike: pass the ring radii, get the coordinates.
|
|
188
|
+
*/
|
|
189
|
+
declare function ringPoints(count: number, cx: number, cy: number, rx: number, ry: number, startAngle?: number): Point[];
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Pure layout math for the card table. No React, no game rules.
|
|
193
|
+
*
|
|
194
|
+
* Everything is derived from one parametric ellipse (see `geometry.ts`). The
|
|
195
|
+
* table, the felt, the rim, the seat ring and the betting line are ellipses
|
|
196
|
+
* sharing a centre and an aspect ratio, and they are all computed **here**.
|
|
197
|
+
*
|
|
198
|
+
* That matters: the betting line used to be computed inside `CardTable` from a
|
|
199
|
+
* module constant while the chips were positioned from the `betRadiusScale`
|
|
200
|
+
* prop, and the two drifted apart. One ellipse, one place that computes it.
|
|
201
|
+
*/
|
|
202
|
+
/** Default design space. */
|
|
203
|
+
declare const DESIGN_W = 1200;
|
|
204
|
+
declare const DESIGN_H = 900;
|
|
205
|
+
/** Rail inset as a fraction of the design box (164/1200, 196/900). */
|
|
206
|
+
declare const DEFAULT_MARGIN_X: number;
|
|
207
|
+
declare const DEFAULT_MARGIN_Y: number;
|
|
208
|
+
/** Gap between the rail and the felt, in design-space px. */
|
|
209
|
+
declare const DEFAULT_FELT_GAP = 11;
|
|
210
|
+
/** Default radius of the bet ring, as a fraction of the table radius. */
|
|
211
|
+
declare const DEFAULT_BET_SCALE = 0.72;
|
|
212
|
+
/** Default minimum clearance between neighbouring seat frames. */
|
|
213
|
+
declare const DEFAULT_SEAT_GAP = 24;
|
|
214
|
+
/** Default preferred seat frame size. */
|
|
215
|
+
declare const DEFAULT_SEAT_W = 160;
|
|
216
|
+
declare const DEFAULT_SEAT_H = 176;
|
|
217
|
+
/** How far inside the rail the rim highlight sits, in design-space px. */
|
|
218
|
+
declare const RIM_GAP = 6;
|
|
219
|
+
/** How far inside the rail the your-turn glow sits, in design-space px. */
|
|
220
|
+
declare const GLOW_GAP = 2;
|
|
221
|
+
interface LayoutOptions {
|
|
222
|
+
/** Number of seats. */
|
|
223
|
+
count: number;
|
|
224
|
+
/** Design-space width / height. */
|
|
225
|
+
designWidth: number;
|
|
226
|
+
designHeight: number;
|
|
227
|
+
/** Rail inset as a fraction of designWidth / designHeight (0..0.5). */
|
|
228
|
+
marginX: number;
|
|
229
|
+
marginY: number;
|
|
230
|
+
/** Felt inset from the rail, in design-space px. */
|
|
231
|
+
feltGap: number;
|
|
232
|
+
/** Preferred (maximum) seat frame size. */
|
|
233
|
+
seatWidth: number;
|
|
234
|
+
seatHeight: number;
|
|
235
|
+
/**
|
|
236
|
+
* Minimum clearance in design-space px to keep between neighbouring seat
|
|
237
|
+
* frames. Frames are shrunk uniformly (never below `SEAT_SHRINK_FLOOR`) to
|
|
238
|
+
* honour it. `0` disables the shrink entirely.
|
|
239
|
+
*/
|
|
240
|
+
seatGap: number;
|
|
241
|
+
/** Angle of seat 0, in degrees. */
|
|
242
|
+
startAngle: number;
|
|
243
|
+
/** Where the betting line sits, as a fraction of the table radii. Default 0.72. */
|
|
244
|
+
betRadiusScale?: number;
|
|
245
|
+
}
|
|
246
|
+
interface SeatLayout {
|
|
247
|
+
index: number;
|
|
248
|
+
/** Centre of the seat frame, in design space. */
|
|
249
|
+
x: number;
|
|
250
|
+
y: number;
|
|
251
|
+
/** Frame size the layout was computed for (≤ the preferred size when shrunk). */
|
|
252
|
+
width: number;
|
|
253
|
+
height: number;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* How far an ellipse sits inside the design box, in design-space px. Spread it
|
|
257
|
+
* onto an absolutely-positioned element and the ellipse lands where the rest of
|
|
258
|
+
* the layout expects.
|
|
259
|
+
*/
|
|
260
|
+
interface EllipseBox {
|
|
261
|
+
top: number;
|
|
262
|
+
right: number;
|
|
263
|
+
bottom: number;
|
|
264
|
+
left: number;
|
|
265
|
+
}
|
|
266
|
+
interface Layout {
|
|
267
|
+
/** Seat centre used for every ring, in design space. */
|
|
268
|
+
cx: number;
|
|
269
|
+
cy: number;
|
|
270
|
+
/** Radii of the outer table ellipse. */
|
|
271
|
+
tableRx: number;
|
|
272
|
+
tableRy: number;
|
|
273
|
+
/** Radii of the seat ring. */
|
|
274
|
+
ringRx: number;
|
|
275
|
+
ringRy: number;
|
|
276
|
+
/** Radii of the betting line. */
|
|
277
|
+
betRx: number;
|
|
278
|
+
betRy: number;
|
|
279
|
+
/** Seat frame size actually used. */
|
|
280
|
+
seatW: number;
|
|
281
|
+
seatH: number;
|
|
282
|
+
seats: SeatLayout[];
|
|
283
|
+
/** Every ellipse the table draws, ready to position. */
|
|
284
|
+
boxes: {
|
|
285
|
+
table: EllipseBox;
|
|
286
|
+
felt: EllipseBox;
|
|
287
|
+
rim: EllipseBox;
|
|
288
|
+
glow: EllipseBox;
|
|
289
|
+
bettingLine: EllipseBox;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/** Smallest fraction of the preferred seat size the auto-shrink will go to. */
|
|
293
|
+
declare const SEAT_SHRINK_FLOOR = 0.5;
|
|
294
|
+
/** Radii of the table ellipse for a given design box and rail inset. */
|
|
295
|
+
declare function tableRadii(designWidth: number, designHeight: number, marginX: number, marginY: number): {
|
|
296
|
+
rx: number;
|
|
297
|
+
ry: number;
|
|
33
298
|
};
|
|
34
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Radii of the seat ring. Seats ride the ring at `+ seatSize / 2 + 6`, so the
|
|
301
|
+
* frames straddle the rail, but never past the design box (seats at the extreme
|
|
302
|
+
* left / right / top / bottom used to be clipped in narrow layouts).
|
|
303
|
+
*/
|
|
304
|
+
declare function seatRingRadii(designWidth: number, designHeight: number, tableRx: number, tableRy: number, seatW: number, seatH: number, feltGap: number): {
|
|
305
|
+
rx: number;
|
|
306
|
+
ry: number;
|
|
307
|
+
};
|
|
308
|
+
/** Centre point of seat `index` on the seat ring, in design space. */
|
|
309
|
+
declare function seatPoint(index: number, count: number, cx: number, cy: number, ringRx: number, ringRy: number, startAngle?: number): {
|
|
310
|
+
x: number;
|
|
311
|
+
y: number;
|
|
312
|
+
};
|
|
313
|
+
/** Turn radii into the inset box of an absolutely-positioned ellipse. */
|
|
314
|
+
declare function ellipseBox(designWidth: number, designHeight: number, rx: number, ry: number): EllipseBox;
|
|
315
|
+
/**
|
|
316
|
+
* Smallest gap between any two seat frames, over every pair. Positive means no
|
|
317
|
+
* frames overlap. Returns `Infinity` for fewer than two seats.
|
|
318
|
+
*/
|
|
319
|
+
declare function minSeatSeparation(seats: SeatLayout[]): number;
|
|
320
|
+
/**
|
|
321
|
+
* Pick the largest uniform scale (1 → `SEAT_SHRINK_FLOOR`, in 1% steps) at which
|
|
322
|
+
* every pair of seat frames clears `seatGap`. Returns the scale to apply.
|
|
323
|
+
*
|
|
324
|
+
* A 10-seat table at the default 160x176 frames only leaves ~26px between
|
|
325
|
+
* neighbours, which reads as crowded; shrinking them a little spreads the ring
|
|
326
|
+
* out. It is also what makes 11–16 seats possible at all. Wide tables (2–8
|
|
327
|
+
* seats) are unaffected — they pass on the first step.
|
|
328
|
+
*/
|
|
329
|
+
declare function fitSeatScale(opts: LayoutOptions): number;
|
|
330
|
+
/**
|
|
331
|
+
* Everything the table draws for one configuration: the table ellipse, the felt,
|
|
332
|
+
* the rim, the glow ring, the betting line, the seat ring and every seat centre.
|
|
333
|
+
*/
|
|
334
|
+
declare function layoutSeats(opts: LayoutOptions): Layout;
|
|
335
|
+
|
|
35
336
|
interface PlayingCardProps {
|
|
36
337
|
/** Compact card code ("As", "10h") or null to render a face-down card back. */
|
|
37
338
|
card: string | null;
|
|
38
339
|
/** xs / sm show a single, larger, centered index; md / lg show corner + center. */
|
|
39
340
|
size?: CardSize;
|
|
341
|
+
/**
|
|
342
|
+
* Accessible name. Defaults to `describeCard(card)` — "Ace of spades" rather
|
|
343
|
+
* than the fragments "A" and "♠" a screen reader would otherwise read out.
|
|
344
|
+
*/
|
|
345
|
+
label?: string;
|
|
40
346
|
className?: string;
|
|
41
347
|
}
|
|
42
348
|
/**
|
|
@@ -44,22 +350,30 @@ interface PlayingCardProps {
|
|
|
44
350
|
* breakpoints) so cards scale predictably inside any container. Small sizes
|
|
45
351
|
* (xs / sm) show only a larger upright index, centered in the card.
|
|
46
352
|
*/
|
|
47
|
-
declare function PlayingCard({ card, size, className }: PlayingCardProps): react.JSX.Element;
|
|
353
|
+
declare function PlayingCard({ card, size, label, className }: PlayingCardProps): react.JSX.Element;
|
|
48
354
|
interface CardBackProps {
|
|
49
355
|
size?: CardSize;
|
|
356
|
+
/** Accessible name. Defaults to "face-down card". */
|
|
357
|
+
label?: string;
|
|
50
358
|
className?: string;
|
|
51
359
|
}
|
|
52
360
|
/** The face-down card back (dark red + gold rosette). */
|
|
53
|
-
declare function CardBack({ size, className }: CardBackProps): react.JSX.Element;
|
|
361
|
+
declare function CardBack({ size, label, className }: CardBackProps): react.JSX.Element;
|
|
54
362
|
interface HoleCardsProps {
|
|
55
363
|
cards: (string | null)[];
|
|
56
364
|
size?: CardSize;
|
|
57
365
|
className?: string;
|
|
58
366
|
/** Animate the cards dealing in, one after another. Default true. */
|
|
59
367
|
deal?: boolean;
|
|
368
|
+
/**
|
|
369
|
+
* Change this to replay the deal animation for an unchanged hand (e.g. a hand
|
|
370
|
+
* id or a round number). Without it, a card whose value changed re-deals on
|
|
371
|
+
* its own, but a new hand holding the same two cards looks identical.
|
|
372
|
+
*/
|
|
373
|
+
dealKey?: string | number;
|
|
60
374
|
}
|
|
61
375
|
/** A row of a player's hole cards. */
|
|
62
|
-
declare function HoleCards({ cards, size, className, deal }: HoleCardsProps): react.JSX.Element;
|
|
376
|
+
declare function HoleCards({ cards, size, className, deal, dealKey }: HoleCardsProps): react.JSX.Element;
|
|
63
377
|
interface CommunityCardsProps {
|
|
64
378
|
cards: (string | null)[];
|
|
65
379
|
size?: CardSize;
|
|
@@ -72,6 +386,126 @@ interface CommunityCardsProps {
|
|
|
72
386
|
/** A row of community cards, each flipping in with a staggered delay. */
|
|
73
387
|
declare function CommunityCards({ cards, size, className, animated, slots }: CommunityCardsProps): react.JSX.Element;
|
|
74
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Chip denominations and the breakdown of an amount into chips. Pure functions,
|
|
391
|
+
* no React — the same split as `geometry.ts` and `layout.ts`.
|
|
392
|
+
*
|
|
393
|
+
* Amounts are in **chip units** (the smallest chip is 1), not in big blinds.
|
|
394
|
+
*/
|
|
395
|
+
interface Denomination {
|
|
396
|
+
/** Face value of the chip. */
|
|
397
|
+
value: number;
|
|
398
|
+
/**
|
|
399
|
+
* Printed on the chip face. Defaults to the value, which is what you want for
|
|
400
|
+
* a 5 or a 100; use it for a "$5" or a "5K" if your table reads differently.
|
|
401
|
+
*/
|
|
402
|
+
label?: string;
|
|
403
|
+
/**
|
|
404
|
+
* CSS colour for the body. Used directly as the SVG `fill`, so it can be any
|
|
405
|
+
* CSS colour — including `var(--color-chip-red)`.
|
|
406
|
+
*/
|
|
407
|
+
color: string;
|
|
408
|
+
/**
|
|
409
|
+
* Colour of the eight edge spots. Defaults to white, or to a dark spot on a
|
|
410
|
+
* light body — see `chipSpotColor`.
|
|
411
|
+
*/
|
|
412
|
+
spotColor?: string;
|
|
413
|
+
/**
|
|
414
|
+
* Optional `fill-*` utility applied on top of `color`. The default set uses
|
|
415
|
+
* these so the `--color-chip-*` theme tokens are actually emitted by Tailwind
|
|
416
|
+
* (it only outputs a theme variable something references) and can therefore be
|
|
417
|
+
* overridden from a consumer's `@theme` block like every other token.
|
|
418
|
+
*
|
|
419
|
+
* A class wins over the `fill` attribute, so `color` acts as the fallback.
|
|
420
|
+
*/
|
|
421
|
+
className?: string;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* The classic low-stakes set: white 1, red 5, blue 10, green 25, black 100.
|
|
425
|
+
*
|
|
426
|
+
* Blue is the one that varies between houses — plenty of rooms use blue for
|
|
427
|
+
* $10 and plenty for $20. Ten is the default here; override
|
|
428
|
+
* `denominations` to use twenty.
|
|
429
|
+
*/
|
|
430
|
+
declare const DEFAULT_DENOMINATIONS: Denomination[];
|
|
431
|
+
/**
|
|
432
|
+
* Spot colour for a chip body: whichever of the two inks is measurably more
|
|
433
|
+
* visible on it.
|
|
434
|
+
*
|
|
435
|
+
* Derived from contrast rather than a luminance threshold, because the obvious
|
|
436
|
+
* implementation — always white spots — puts the white chip
|
|
437
|
+
* (`--color-chip-white`, #ecf0f1) at a 1.13:1 contrast ratio against its own
|
|
438
|
+
* body, so it renders as a blank oval with no edge at all. Colours we cannot
|
|
439
|
+
* measure keep white spots.
|
|
440
|
+
*/
|
|
441
|
+
declare function chipSpotColor(bodyColor: string): string;
|
|
442
|
+
interface ChipStackEntry extends Denomination {
|
|
443
|
+
/** How many chips of this denomination the amount needs. */
|
|
444
|
+
count: number;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Break an amount into chips, largest denomination first (the greedy breakdown
|
|
448
|
+
* a dealer actually uses).
|
|
449
|
+
*
|
|
450
|
+
* Anything below the smallest denomination is left out rather than rounded up —
|
|
451
|
+
* `chipsTotal()` tells you what the chips add up to, so you can spot the
|
|
452
|
+
* remainder. Non-integer and negative amounts are floored/clamped to 0.
|
|
453
|
+
*/
|
|
454
|
+
declare function chipBreakdown(amount: number, denominations?: Denomination[]): ChipStackEntry[];
|
|
455
|
+
/** What a set of chip entries adds up to. Compare with the amount to see the remainder. */
|
|
456
|
+
declare function chipsTotal(entries: ChipStackEntry[]): number;
|
|
457
|
+
/** Total number of physical chips in a breakdown. */
|
|
458
|
+
declare function chipCount(entries: ChipStackEntry[]): number;
|
|
459
|
+
|
|
460
|
+
interface ChipProps {
|
|
461
|
+
/** Printed on the centre of the face. */
|
|
462
|
+
value?: number | string;
|
|
463
|
+
/** CSS colour for the body. */
|
|
464
|
+
color?: string;
|
|
465
|
+
/** `fill-*` utility applied over `color` — see `Denomination.className`. */
|
|
466
|
+
colorClassName?: string;
|
|
467
|
+
/** Colour of the eight edge spots. Defaults to `chipSpotColor(color)`. */
|
|
468
|
+
spotColor?: string;
|
|
469
|
+
/** Default `"md"`. */
|
|
470
|
+
size?: ChipSize;
|
|
471
|
+
/** Accessible name. Defaults to `"<value> chip"`. */
|
|
472
|
+
label?: string;
|
|
473
|
+
className?: string;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* A single chip, drawn as SVG: a coloured disc with a darker edge for
|
|
477
|
+
* thickness, eight white edge spots, a rim highlight and a white centre carrying
|
|
478
|
+
* the value.
|
|
479
|
+
*
|
|
480
|
+
* The colours come from `--color-chip-*` via `fill-*` utilities, so the chip
|
|
481
|
+
* palette is themeable like every other token.
|
|
482
|
+
*/
|
|
483
|
+
declare function Chip({ value, color, colorClassName, spotColor, size, label, className, }: ChipProps): react.JSX.Element;
|
|
484
|
+
interface ChipStackProps {
|
|
485
|
+
/** Amount in chip units — see `chipBreakdown`. */
|
|
486
|
+
amount: number;
|
|
487
|
+
/** Default `"md"`. */
|
|
488
|
+
size?: ChipSize;
|
|
489
|
+
/** Default `DEFAULT_DENOMINATIONS` (white 1, red 5, blue 10, green 25, black 100). */
|
|
490
|
+
denominations?: Denomination[];
|
|
491
|
+
/**
|
|
492
|
+
* Most chips to draw, biggest denominations first. A 10,000 bet would
|
|
493
|
+
* otherwise be 100 black chips. The count of the chips left off is shown
|
|
494
|
+
* underneath. Default 12.
|
|
495
|
+
*/
|
|
496
|
+
maxChips?: number;
|
|
497
|
+
/** Content under the stack — usually the formatted amount. */
|
|
498
|
+
label?: ReactNode;
|
|
499
|
+
className?: string;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* The chips for an amount: a real stack, largest denomination on top.
|
|
503
|
+
*
|
|
504
|
+
* The accessible name is the full breakdown ("240 in 4 chips: 2x100, 1x25,
|
|
505
|
+
* 1x10"), so a screen reader gets the value rather than four anonymous images.
|
|
506
|
+
*/
|
|
507
|
+
declare function ChipStack({ amount, size, denominations, maxChips, label, className, }: ChipStackProps): react.JSX.Element;
|
|
508
|
+
|
|
75
509
|
interface SeatProps {
|
|
76
510
|
/** Turn/active highlight (generic). Animated by default. */
|
|
77
511
|
isActive?: boolean;
|
|
@@ -105,7 +539,13 @@ interface SeatProps {
|
|
|
105
539
|
topRight?: ReactNode;
|
|
106
540
|
/** Slot: floating content centered above the top edge (e.g. D/SB/BB badges). */
|
|
107
541
|
topCenter?: ReactNode;
|
|
542
|
+
/**
|
|
543
|
+
* Frame width. Defaults to the size the surrounding `CardTable` laid out for
|
|
544
|
+
* this seat, or 160 outside a table — the table is the one that knows how much
|
|
545
|
+
* room the ring has.
|
|
546
|
+
*/
|
|
108
547
|
width?: number;
|
|
548
|
+
/** Frame height. Defaults to the laid-out size, or 176 outside a table. */
|
|
109
549
|
height?: number;
|
|
110
550
|
className?: string;
|
|
111
551
|
/** Extra classes for the inner frame, to override visuals. */
|
|
@@ -125,7 +565,10 @@ interface PotProps {
|
|
|
125
565
|
amount: number;
|
|
126
566
|
/** Optional label, e.g. "Main" / "Side". */
|
|
127
567
|
label?: string;
|
|
128
|
-
/**
|
|
568
|
+
/**
|
|
569
|
+
* Formatter for the amount. Defaults to a plain, locale-grouped number —
|
|
570
|
+
* pass one if your amounts have a unit.
|
|
571
|
+
*/
|
|
129
572
|
format?: (amount: number) => string;
|
|
130
573
|
/** Animate the amount counting up when it changes. Default true. */
|
|
131
574
|
animate?: boolean;
|
|
@@ -134,14 +577,54 @@ interface PotProps {
|
|
|
134
577
|
/** A small, standalone pot pill. Optional — the table does not require it. */
|
|
135
578
|
declare function Pot({ amount, label, format, animate, className }: PotProps): react.JSX.Element;
|
|
136
579
|
|
|
580
|
+
/** Geometry CardTable laid a seat frame out for, handed to `renderSeat`. */
|
|
581
|
+
interface SeatBox {
|
|
582
|
+
index: number;
|
|
583
|
+
isYou: boolean;
|
|
584
|
+
/** Centre of the frame, in design space. */
|
|
585
|
+
x: number;
|
|
586
|
+
y: number;
|
|
587
|
+
/** Frame size the ring was laid out for (≤ `seatWidth`/`seatHeight`). */
|
|
588
|
+
width: number;
|
|
589
|
+
height: number;
|
|
590
|
+
}
|
|
137
591
|
interface CardTableProps {
|
|
138
|
-
/**
|
|
592
|
+
/**
|
|
593
|
+
* Number of seats. Default 6.
|
|
594
|
+
*
|
|
595
|
+
* There is no upper limit. The seat ring shrinks the frames until `seatGap` is
|
|
596
|
+
* honoured, so 11–16 seats lay out fine (measured: 11 -> 126x139 frames,
|
|
597
|
+
* 16 -> 80x88). Past roughly 16 the frames hit the 50% shrink floor and start
|
|
598
|
+
* to overlap — that is the caller's call, not something to clamp silently.
|
|
599
|
+
*/
|
|
139
600
|
seatCount?: number;
|
|
140
601
|
/** Optional per-seat content. Length should equal `seatCount`. */
|
|
141
602
|
seats?: (ReactNode | null)[];
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
|
|
603
|
+
/**
|
|
604
|
+
* Alternative to `seats`: render a seat by index. Receives the laid-out
|
|
605
|
+
* frame box as a third argument, so an auto-shrunk seat can be sized to
|
|
606
|
+
* match the ring: `<Seat width={box.width} height={box.height} />`.
|
|
607
|
+
*/
|
|
608
|
+
renderSeat?: (index: number, isYou: boolean, box: SeatBox) => ReactNode;
|
|
609
|
+
/**
|
|
610
|
+
* Optional per-seat "bet" content, auto-positioned on the betting line
|
|
611
|
+
* (`betRadiusScale`) along the seat-to-center radius, so it faces the middle
|
|
612
|
+
* of the table. Length should equal `seatCount`; `null` entries are skipped.
|
|
613
|
+
*/
|
|
614
|
+
bets?: (ReactNode | null)[];
|
|
615
|
+
/** Alternative to `bets`: render a bet by index (return `null` to skip). */
|
|
616
|
+
renderBet?: (index: number) => ReactNode;
|
|
617
|
+
/**
|
|
618
|
+
* Radius of the bet ring, as a fraction of the table radius. Default 0.72.
|
|
619
|
+
* The drawn betting line follows this value. Clamped to 0.2–1.
|
|
620
|
+
*/
|
|
621
|
+
betRadiusScale?: number;
|
|
622
|
+
/**
|
|
623
|
+
* Angle (degrees) of seat 0. Default 90° = directly below the table centre.
|
|
624
|
+
* Seats advance by `360 / seatCount` from there, so 2–10 players spread evenly.
|
|
625
|
+
*/
|
|
626
|
+
startAngle?: number;
|
|
627
|
+
/** Which seat is the viewer (adds no offset; only flags `isYou`). Default null. */
|
|
145
628
|
yourSeatIndex?: number | null;
|
|
146
629
|
/** Optional content in the middle of the table (cards, pot, anything). */
|
|
147
630
|
center?: ReactNode;
|
|
@@ -153,16 +636,57 @@ interface CardTableProps {
|
|
|
153
636
|
winnerSeats?: number[];
|
|
154
637
|
/** Show confetti for winners. Default true. */
|
|
155
638
|
confetti?: boolean;
|
|
156
|
-
/**
|
|
639
|
+
/**
|
|
640
|
+
* Confetti colours. Defaults to a party palette. This is a prop rather than a
|
|
641
|
+
* theme token because the colours land in inline styles, which Tailwind's
|
|
642
|
+
* tree-shaking cannot see — a custom property nothing references never gets
|
|
643
|
+
* emitted.
|
|
644
|
+
*/
|
|
645
|
+
confettiColors?: string[];
|
|
646
|
+
/** Preferred seat frame width. Default 160. */
|
|
157
647
|
seatWidth?: number;
|
|
648
|
+
/** Preferred seat frame height. Default 176. */
|
|
158
649
|
seatHeight?: number;
|
|
650
|
+
/**
|
|
651
|
+
* Minimum clearance to keep between neighbouring seat frames, in design-space
|
|
652
|
+
* px. Frames shrink uniformly (down to 50%) until it is honoured, which is what
|
|
653
|
+
* keeps 9–10 seat tables from looking cramped and what lets 11–16 seat tables
|
|
654
|
+
* lay out at all. `0` disables it.
|
|
655
|
+
* Only applies when `renderSeat` is used (it is the only form that can size
|
|
656
|
+
* the frames); the `seats` array keeps the size you were given.
|
|
657
|
+
* Default 24.
|
|
658
|
+
*/
|
|
659
|
+
seatGap?: number;
|
|
660
|
+
/** Design-space width. Default 1200. */
|
|
661
|
+
designWidth?: number;
|
|
662
|
+
/** Design-space height. Default 900. */
|
|
663
|
+
designHeight?: number;
|
|
664
|
+
/**
|
|
665
|
+
* Rail inset as a fraction of the design box (default 164/1200).
|
|
666
|
+
* Proportional so a custom design box stays in proportion.
|
|
667
|
+
*/
|
|
668
|
+
marginX?: number;
|
|
669
|
+
/** Rail inset as a fraction of the design box (default 196/900). */
|
|
670
|
+
marginY?: number;
|
|
671
|
+
/** Felt inset from the rail, in design-space px. Default 11. */
|
|
672
|
+
feltGap?: number;
|
|
673
|
+
/**
|
|
674
|
+
* How the design box relates to the container.
|
|
675
|
+
* - `"design"` (default): scale the fixed design box to fit — preserves the
|
|
676
|
+
* table's proportions but letterboxes non-matching containers.
|
|
677
|
+
* - `"container"`: derive the design width from the container's aspect ratio
|
|
678
|
+
* (clamped to 0.5–2.5) so the table fills the box instead of letterboxing.
|
|
679
|
+
* Use this for phone-portrait layouts. Pair with `designHeight` to set the
|
|
680
|
+
* scale.
|
|
681
|
+
*/
|
|
682
|
+
fit?: "design" | "container";
|
|
159
683
|
className?: string;
|
|
160
684
|
}
|
|
161
685
|
/**
|
|
162
|
-
* A responsive, game-agnostic card table. It scales a
|
|
163
|
-
* onto its container and arranges `seatCount` seats around the felt.
|
|
164
|
-
* presentational: supply seat content and an optional `center` slot.
|
|
686
|
+
* A responsive, game-agnostic card table. It scales a design box (1200x900 by
|
|
687
|
+
* default) onto its container and arranges `seatCount` seats around the felt.
|
|
688
|
+
* Purely presentational: supply seat content and an optional `center` slot.
|
|
165
689
|
*/
|
|
166
|
-
declare function CardTable({ seatCount, seats, renderSeat, yourSeatIndex, center, ambient, yourTurn, winnerSeats, confetti, seatWidth, seatHeight, className, }: CardTableProps): react.JSX.Element;
|
|
690
|
+
declare function CardTable({ seatCount, seats, renderSeat, bets, renderBet, betRadiusScale, startAngle, yourSeatIndex, center, ambient, yourTurn, winnerSeats, confetti, confettiColors, seatWidth, seatHeight, seatGap, designWidth, designHeight, marginX, marginY, feltGap, fit, className, }: CardTableProps): react.JSX.Element;
|
|
167
691
|
|
|
168
|
-
export { BB, CardBack, type CardBackProps, type CardSize, CardTable, type CardTableProps, CommunityCards, type CommunityCardsProps, HoleCards, type HoleCardsProps, type NextSeatOptions, PlayingCard, type PlayingCardProps, CardTable as PokerTable, type CardTableProps as PokerTableProps, Pot, type PotProps, Seat, type SeatProps, cn, formatBB, getSuitSymbol, nextSeatIndex, parseCard };
|
|
692
|
+
export { BB, COUNT_UP_MS, CardBack, type CardBackProps, type CardSize, CardTable, type CardTableProps, Chip, type ChipProps, type ChipSize, ChipStack, type ChipStackEntry, type ChipStackProps, CommunityCards, type CommunityCardsProps, DEAL_STAGGER_MS, DEFAULT_BET_SCALE, DEFAULT_DENOMINATIONS, DEFAULT_FELT_GAP, DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y, DEFAULT_SEAT_GAP, DEFAULT_SEAT_H, DEFAULT_SEAT_W, DESIGN_H, DESIGN_W, type Denomination, type EllipseBox, FLIP_STAGGER_MS, GLOW_GAP, HoleCards, type HoleCardsProps, type Layout, type LayoutOptions, type NextSeatOptions, type ParsedCard, PlayingCard, type PlayingCardProps, type Point, CardTable as PokerTable, type CardTableProps as PokerTableProps, Pot, type PotProps, RIM_GAP, type Rgba, SEAT_SHRINK_FLOOR, SIZES, Seat, type SeatBox, type SeatLayout, type SeatProps, type Size, chipBreakdown, chipCount, chipSpotColor, chipsTotal, cn, contrastRatio, describeCard, ellipseBox, ellipsePoint, fitSeatScale, flatten, formatBB, getRankName, getSuitName, getSuitSymbol, isKnownSuit, isRedSuit, layoutSeats, minSeatSeparation, mostVisible, nextSeatIndex, parseCard, parseColor, relativeLuminance, ringPoints, seatAngle, seatPoint, seatRingRadii, tableRadii };
|