casino-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.md +213 -0
  2. package/dist/assets/assets/card-back.svg +43 -0
  3. package/dist/assets/assets/card-shoe-overlay.svg +14 -0
  4. package/dist/assets/card-back.svg +43 -0
  5. package/dist/assets/card-shoe-overlay.svg +14 -0
  6. package/dist/casino-ui.css +2 -0
  7. package/dist/components/bet-panel.d.ts +41 -0
  8. package/dist/components/bet-panel.d.ts.map +1 -0
  9. package/dist/components/bet-panel.js +37 -0
  10. package/dist/components/bet-panel.js.map +1 -0
  11. package/dist/components/button.d.ts +15 -0
  12. package/dist/components/button.d.ts.map +1 -0
  13. package/dist/components/button.js +27 -0
  14. package/dist/components/button.js.map +1 -0
  15. package/dist/components/card-score-badge.d.ts +9 -0
  16. package/dist/components/card-score-badge.d.ts.map +1 -0
  17. package/dist/components/card-score-badge.js +20 -0
  18. package/dist/components/card-score-badge.js.map +1 -0
  19. package/dist/components/card-shoe.d.ts +6 -0
  20. package/dist/components/card-shoe.d.ts.map +1 -0
  21. package/dist/components/card-shoe.js +14 -0
  22. package/dist/components/card-shoe.js.map +1 -0
  23. package/dist/components/card-table.d.ts +7 -0
  24. package/dist/components/card-table.d.ts.map +1 -0
  25. package/dist/components/card-table.js +6 -0
  26. package/dist/components/card-table.js.map +1 -0
  27. package/dist/components/chip-rack.d.ts +6 -0
  28. package/dist/components/chip-rack.d.ts.map +1 -0
  29. package/dist/components/chip-rack.js +13 -0
  30. package/dist/components/chip-rack.js.map +1 -0
  31. package/dist/components/chip.d.ts +49 -0
  32. package/dist/components/chip.d.ts.map +1 -0
  33. package/dist/components/chip.js +81 -0
  34. package/dist/components/chip.js.map +1 -0
  35. package/dist/components/game-footer.d.ts +29 -0
  36. package/dist/components/game-footer.d.ts.map +1 -0
  37. package/dist/components/game-footer.js +72 -0
  38. package/dist/components/game-footer.js.map +1 -0
  39. package/dist/components/game-info-dialog.d.ts +23 -0
  40. package/dist/components/game-info-dialog.d.ts.map +1 -0
  41. package/dist/components/game-info-dialog.js +26 -0
  42. package/dist/components/game-info-dialog.js.map +1 -0
  43. package/dist/components/icon.d.ts +10 -0
  44. package/dist/components/icon.d.ts.map +1 -0
  45. package/dist/components/icon.js +59 -0
  46. package/dist/components/icon.js.map +1 -0
  47. package/dist/components/poker-card.d.ts +15 -0
  48. package/dist/components/poker-card.d.ts.map +1 -0
  49. package/dist/components/poker-card.js +77 -0
  50. package/dist/components/poker-card.js.map +1 -0
  51. package/dist/components/result-badge.d.ts +14 -0
  52. package/dist/components/result-badge.d.ts.map +1 -0
  53. package/dist/components/result-badge.js +71 -0
  54. package/dist/components/result-badge.js.map +1 -0
  55. package/dist/components/switch-table-dialog.d.ts +10 -0
  56. package/dist/components/switch-table-dialog.d.ts.map +1 -0
  57. package/dist/components/switch-table-dialog.js +51 -0
  58. package/dist/components/switch-table-dialog.js.map +1 -0
  59. package/dist/components/toast-container.d.ts +8 -0
  60. package/dist/components/toast-container.d.ts.map +1 -0
  61. package/dist/components/toast-container.js +8 -0
  62. package/dist/components/toast-container.js.map +1 -0
  63. package/dist/components/toast-helpers.d.ts +22 -0
  64. package/dist/components/toast-helpers.d.ts.map +1 -0
  65. package/dist/components/toast-helpers.js +47 -0
  66. package/dist/components/toast-helpers.js.map +1 -0
  67. package/dist/components/toast.d.ts +26 -0
  68. package/dist/components/toast.d.ts.map +1 -0
  69. package/dist/components/toast.js +49 -0
  70. package/dist/components/toast.js.map +1 -0
  71. package/dist/components/ui/dialog.d.ts +14 -0
  72. package/dist/components/ui/dialog.d.ts.map +1 -0
  73. package/dist/components/ui/dialog.js +35 -0
  74. package/dist/components/ui/dialog.js.map +1 -0
  75. package/dist/components/ui/drawer.d.ts +14 -0
  76. package/dist/components/ui/drawer.d.ts.map +1 -0
  77. package/dist/components/ui/drawer.js +35 -0
  78. package/dist/components/ui/drawer.js.map +1 -0
  79. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  80. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  81. package/dist/components/ui/dropdown-menu.js +39 -0
  82. package/dist/components/ui/dropdown-menu.js.map +1 -0
  83. package/dist/index.d.ts +23 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +25 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/lib/utils.d.ts +3 -0
  88. package/dist/lib/utils.d.ts.map +1 -0
  89. package/dist/lib/utils.js +6 -0
  90. package/dist/lib/utils.js.map +1 -0
  91. package/dist/stories/BetPanel.stories.d.ts +10 -0
  92. package/dist/stories/BetPanel.stories.d.ts.map +1 -0
  93. package/dist/stories/BetPanel.stories.js +34 -0
  94. package/dist/stories/BetPanel.stories.js.map +1 -0
  95. package/dist/stories/Button.stories.d.ts +13 -0
  96. package/dist/stories/Button.stories.d.ts.map +1 -0
  97. package/dist/stories/Button.stories.js +39 -0
  98. package/dist/stories/Button.stories.js.map +1 -0
  99. package/dist/stories/CardScoreBadge.stories.d.ts +8 -0
  100. package/dist/stories/CardScoreBadge.stories.d.ts.map +1 -0
  101. package/dist/stories/CardScoreBadge.stories.js +21 -0
  102. package/dist/stories/CardScoreBadge.stories.js.map +1 -0
  103. package/dist/stories/CardShoe.stories.d.ts +7 -0
  104. package/dist/stories/CardShoe.stories.d.ts.map +1 -0
  105. package/dist/stories/CardShoe.stories.js +8 -0
  106. package/dist/stories/CardShoe.stories.js.map +1 -0
  107. package/dist/stories/CardTable.stories.d.ts +7 -0
  108. package/dist/stories/CardTable.stories.d.ts.map +1 -0
  109. package/dist/stories/CardTable.stories.js +15 -0
  110. package/dist/stories/CardTable.stories.js.map +1 -0
  111. package/dist/stories/Chip.stories.d.ts +11 -0
  112. package/dist/stories/Chip.stories.d.ts.map +1 -0
  113. package/dist/stories/Chip.stories.js +32 -0
  114. package/dist/stories/Chip.stories.js.map +1 -0
  115. package/dist/stories/ChipRack.stories.d.ts +7 -0
  116. package/dist/stories/ChipRack.stories.d.ts.map +1 -0
  117. package/dist/stories/ChipRack.stories.js +8 -0
  118. package/dist/stories/ChipRack.stories.js.map +1 -0
  119. package/dist/stories/Dialog.stories.d.ts +6 -0
  120. package/dist/stories/Dialog.stories.d.ts.map +1 -0
  121. package/dist/stories/Dialog.stories.js +15 -0
  122. package/dist/stories/Dialog.stories.js.map +1 -0
  123. package/dist/stories/GameFooter.stories.d.ts +7 -0
  124. package/dist/stories/GameFooter.stories.d.ts.map +1 -0
  125. package/dist/stories/GameFooter.stories.js +37 -0
  126. package/dist/stories/GameFooter.stories.js.map +1 -0
  127. package/dist/stories/Icon.stories.d.ts +9 -0
  128. package/dist/stories/Icon.stories.d.ts.map +1 -0
  129. package/dist/stories/Icon.stories.js +27 -0
  130. package/dist/stories/Icon.stories.js.map +1 -0
  131. package/dist/stories/PokerCard.stories.d.ts +14 -0
  132. package/dist/stories/PokerCard.stories.d.ts.map +1 -0
  133. package/dist/stories/PokerCard.stories.js +40 -0
  134. package/dist/stories/PokerCard.stories.js.map +1 -0
  135. package/dist/stories/ResultBadge.stories.d.ts +11 -0
  136. package/dist/stories/ResultBadge.stories.d.ts.map +1 -0
  137. package/dist/stories/ResultBadge.stories.js +32 -0
  138. package/dist/stories/ResultBadge.stories.js.map +1 -0
  139. package/dist/stories/Toast.stories.d.ts +10 -0
  140. package/dist/stories/Toast.stories.d.ts.map +1 -0
  141. package/dist/stories/Toast.stories.js +34 -0
  142. package/dist/stories/Toast.stories.js.map +1 -0
  143. package/dist/styles/casino-ui-base.css +310 -0
  144. package/dist/styles/styles/casino-ui-base.css +310 -0
  145. package/dist/styles/styles/toast.css +52 -0
  146. package/dist/styles/toast.css +52 -0
  147. package/dist/toast.css +52 -0
  148. package/dist/types.d.ts +11 -0
  149. package/dist/types.d.ts.map +1 -0
  150. package/dist/types.js +2 -0
  151. package/dist/types.js.map +1 -0
  152. package/dist/utils/format-amount.d.ts +2 -0
  153. package/dist/utils/format-amount.d.ts.map +1 -0
  154. package/dist/utils/format-amount.js +31 -0
  155. package/dist/utils/format-amount.js.map +1 -0
  156. package/package.json +58 -0
  157. package/src/assets/card-back.svg +43 -0
  158. package/src/assets/card-shoe-overlay.svg +14 -0
  159. package/src/components/bet-panel.tsx +275 -0
  160. package/src/components/button.tsx +80 -0
  161. package/src/components/card-score-badge.tsx +61 -0
  162. package/src/components/card-shoe.tsx +52 -0
  163. package/src/components/card-table.tsx +182 -0
  164. package/src/components/chip-rack.tsx +55 -0
  165. package/src/components/chip.tsx +203 -0
  166. package/src/components/game-footer.tsx +356 -0
  167. package/src/components/game-info-dialog.tsx +245 -0
  168. package/src/components/icon.tsx +94 -0
  169. package/src/components/poker-card.tsx +192 -0
  170. package/src/components/result-badge.tsx +157 -0
  171. package/src/components/switch-table-dialog.tsx +211 -0
  172. package/src/components/toast-container.tsx +25 -0
  173. package/src/components/toast-helpers.ts +79 -0
  174. package/src/components/toast.tsx +282 -0
  175. package/src/components/ui/dialog.tsx +134 -0
  176. package/src/components/ui/drawer.tsx +132 -0
  177. package/src/components/ui/dropdown-menu.tsx +210 -0
  178. package/src/env.d.ts +6 -0
  179. package/src/index.ts +88 -0
  180. package/src/lib/utils.ts +6 -0
  181. package/src/stories/BetPanel.stories.tsx +113 -0
  182. package/src/stories/Button.stories.tsx +55 -0
  183. package/src/stories/CardScoreBadge.stories.tsx +34 -0
  184. package/src/stories/CardShoe.stories.tsx +12 -0
  185. package/src/stories/Chip.stories.tsx +51 -0
  186. package/src/stories/ChipRack.stories.tsx +12 -0
  187. package/src/stories/Dialog.stories.tsx +45 -0
  188. package/src/stories/GameFooter.stories.tsx +45 -0
  189. package/src/stories/Icon.stories.tsx +49 -0
  190. package/src/stories/PokerCard.stories.tsx +72 -0
  191. package/src/stories/ResultBadge.stories.tsx +51 -0
  192. package/src/stories/Toast.stories.tsx +71 -0
  193. package/src/styles/casino-ui-base.css +310 -0
  194. package/src/styles/toast.css +52 -0
  195. package/src/types.ts +11 -0
  196. package/src/utils/format-amount.ts +35 -0
@@ -0,0 +1,275 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+ import type { CoinInfo } from "../types";
4
+ /* -------------------------------------------------------------------------- */
5
+ /* Betting Mode Components */
6
+ /* -------------------------------------------------------------------------- */
7
+
8
+ interface BetPanelRootProps extends React.ComponentProps<"div"> {
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ function BetPanelRoot({ className, children, ...props }: BetPanelRootProps) {
13
+ return (
14
+ <div
15
+ data-slot="bet-panel"
16
+ className={cn(
17
+ "flex flex-col justify-end items-center rounded-xl bg-panel-bg p-3 sm:px-5 sm:py-4 overflow-x-clip",
18
+ className,
19
+ )}
20
+ {...props}
21
+ >
22
+ {children}
23
+ </div>
24
+ );
25
+ }
26
+
27
+ interface BetPanelFoldingInfoProps extends React.ComponentProps<"div"> {
28
+ open: boolean;
29
+ children: React.ReactNode;
30
+ }
31
+
32
+ function BetPanelFoldingInfo({
33
+ className,
34
+ open,
35
+ children,
36
+ ...props
37
+ }: BetPanelFoldingInfoProps) {
38
+ return (
39
+ <div
40
+ className={cn(
41
+ "w-full grid duration-300 ease-in-out",
42
+ open ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
43
+ className,
44
+ )}
45
+ >
46
+ <div className="overflow-hidden">{children}</div>
47
+ </div>
48
+ );
49
+ }
50
+
51
+ interface BetPanelInputProps extends React.ComponentProps<"div"> {
52
+ label?: string;
53
+ balance?: string;
54
+ coinInfo?: CoinInfo;
55
+ value?: string;
56
+ setValue: React.Dispatch<React.SetStateAction<string>>;
57
+ setOriginalBetSize: (size: number | ((prev: number) => number)) => void;
58
+ getMaxBet: () => number;
59
+ error?: string;
60
+ setError: (error?: string) => void;
61
+ disabled?: boolean;
62
+ }
63
+
64
+ function BetPanelInput({
65
+ className,
66
+ label = "Balance",
67
+ balance,
68
+ coinInfo,
69
+ value,
70
+ setValue,
71
+ getMaxBet,
72
+ setOriginalBetSize,
73
+ error,
74
+ setError,
75
+ disabled,
76
+ ...props
77
+ }: BetPanelInputProps) {
78
+ const isUSD = coinInfo?.coinType?.toLowerCase().includes("usd") ?? false;
79
+ const regex = /^[0-9.]+$/;
80
+
81
+ return (
82
+ <div
83
+ data-slot="bet-panel-input"
84
+ className={cn(
85
+ "relative flex flex-1 sm:flex-none sm:w-[234px] shrink-0 flex-col gap-1",
86
+ disabled && "opacity-60 pointer-events-none",
87
+ className,
88
+ )}
89
+ {...props}
90
+ >
91
+ <div className="hidden sm:flex items-center justify-between leading-4.5 h-4.5 text-sm font-semibold text-[#BFBFBF]">
92
+ <span>{label}</span>
93
+ {balance != null && (
94
+ <div className="flex items-center gap-2">
95
+ <span>{balance}</span>
96
+ <span>{coinInfo?.symbol}</span>
97
+ </div>
98
+ )}
99
+ </div>
100
+
101
+ <div className="relative flex flex-col items-center">
102
+ <div
103
+ className={cn(
104
+ "flex w-full items-center rounded-lg sm:rounded-xl border bg-surface-600 h-10 sm:h-12 px-3 py-2 transition-colors",
105
+ error ? "border-accent-error" : "border-button-grey",
106
+ )}
107
+ >
108
+ <div className="flex flex-1 items-center gap-1">
109
+ {isUSD ? (
110
+ <span>$</span>
111
+ ) : coinInfo?.image ? (
112
+ typeof coinInfo.image === "string" ? (
113
+ <img
114
+ src={coinInfo.image}
115
+ alt={coinInfo.symbol}
116
+ className="size-4 object-contain"
117
+ />
118
+ ) : (
119
+ <div className="min-w-4 h-4 w-4 object-contain">
120
+ {coinInfo.image}
121
+ </div>
122
+ )
123
+ ) : null}
124
+ <input
125
+ type="text"
126
+ inputMode="decimal"
127
+ value={value ?? ""}
128
+ onChange={(e) => {
129
+ if (
130
+ regex.test(e.target.value) ||
131
+ e.target.value === "" ||
132
+ e.target.value === "."
133
+ ) {
134
+ setValue?.(e.target.value);
135
+ }
136
+ }}
137
+ className="w-full min-w-0 bg-transparent text-base font-semibold text-white tracking-tight outline-none placeholder:text-white/30"
138
+ placeholder="0.00"
139
+ />
140
+ </div>
141
+
142
+ <div className="flex items-center gap-1 shrink-0">
143
+ <button
144
+ type="button"
145
+ onClick={() => setValue((Number(value) * 2).toString())}
146
+ className="hidden sm:block rounded-lg bg-surface-300 px-2.5 py-2 text-xs font-semibold text-white transition-colors hover:bg-action-hover active:bg-action-active"
147
+ >
148
+ 2x
149
+ </button>
150
+ <button
151
+ type="button"
152
+ onClick={() => setValue(getMaxBet().toString())}
153
+ className="rounded-md sm:rounded-lg bg-surface-300 px-2 sm:px-2.5 py-1 sm:py-2 text-xs font-semibold text-white transition-colors hover:bg-action-hover active:bg-action-active"
154
+ >
155
+ MAX
156
+ </button>
157
+ </div>
158
+ </div>
159
+
160
+ <div
161
+ className={cn(
162
+ "absolute top-10 sm:top-12 left-1/2 -translate-x-1/2 grid w-full transition-[grid-template-rows] duration-300 ease-out z-30",
163
+ error ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
164
+ )}
165
+ >
166
+ <div className="overflow-hidden">
167
+ <div className="mx-auto flex w-[90%] sm:w-[210px] items-center justify-center rounded-b-xl bg-accent-error py-1 px-2.5 sm:py-2">
168
+ <span className="text-center text-xs font-semibold text-white">
169
+ {error}
170
+ </span>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ );
177
+ }
178
+
179
+ interface BetPanelChipsProps extends React.ComponentProps<"div"> {
180
+ disabled?: boolean;
181
+ }
182
+
183
+ function BetPanelChips({ className, disabled, ...props }: BetPanelChipsProps) {
184
+ return (
185
+ <div
186
+ data-slot="bet-panel-chips"
187
+ className={cn(
188
+ "min-w-0 w-full flex h-full items-center justify-between sm:justify-start sm:gap-1 transition-opacity",
189
+ disabled && "opacity-60 pointer-events-none",
190
+ className,
191
+ )}
192
+ {...props}
193
+ />
194
+ );
195
+ }
196
+
197
+ interface BetPanelActionsProps extends React.ComponentProps<"div"> {
198
+ disabled?: boolean;
199
+ }
200
+
201
+ function BetPanelActions({
202
+ className,
203
+ disabled,
204
+ ...props
205
+ }: BetPanelActionsProps) {
206
+ return (
207
+ <div
208
+ data-slot="bet-panel-actions"
209
+ className={cn(
210
+ "flex items-center gap-2 shrink-0 transition-opacity select-none",
211
+ disabled && "opacity-50 pointer-events-none",
212
+ className,
213
+ )}
214
+ {...props}
215
+ />
216
+ );
217
+ }
218
+
219
+ /* -------------------------------------------------------------------------- */
220
+ /* Timer Component (shared by Action & Insurance panels) */
221
+ /* -------------------------------------------------------------------------- */
222
+
223
+ interface BetPanelTimerProps {
224
+ remainingTime: number;
225
+ progress: number;
226
+ isWarning: boolean;
227
+ isActive: boolean;
228
+ }
229
+
230
+ function BetPanelTimer({
231
+ remainingTime,
232
+ progress,
233
+ isWarning,
234
+ isActive,
235
+ }: BetPanelTimerProps) {
236
+ if (!isActive) return null;
237
+
238
+ return (
239
+ <div className="flex h-6 w-full items-center gap-3 py-1">
240
+ <span
241
+ className={cn(
242
+ "shrink-0 text-xs tabular-nums",
243
+ isWarning ? "text-red-500 animate-pulse" : "text-accent-success",
244
+ )}
245
+ style={{ fontFamily: "var(--font-dm-mono)", fontWeight: 500 }}
246
+ >
247
+ {remainingTime}s
248
+ </span>
249
+ <div className="relative h-1.5 flex-1 rounded-full bg-surface-650">
250
+ <div
251
+ className={cn(
252
+ "absolute inset-y-0 left-0 rounded-full transition-[width] duration-1000 ease-linear shadow-[inset_0px_1px_2px_0px_rgba(255,255,255,0.15)]",
253
+ isWarning
254
+ ? "bg-red-500"
255
+ : "bg-linear-to-r from-accent-success to-timer-end",
256
+ )}
257
+ style={{ width: `${progress}%` }}
258
+ />
259
+ </div>
260
+ </div>
261
+ );
262
+ }
263
+
264
+ /* -------------------------------------------------------------------------- */
265
+ /* Exports */
266
+ /* -------------------------------------------------------------------------- */
267
+
268
+ export {
269
+ BetPanelTimer,
270
+ BetPanelInput,
271
+ BetPanelActions,
272
+ BetPanelChips,
273
+ BetPanelRoot,
274
+ BetPanelFoldingInfo,
275
+ };
@@ -0,0 +1,80 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "flex h-11 items-center justify-center border-2 rounded-lg cursor-pointer whitespace-nowrap select-none disabled:pointer-events-none disabled:opacity-50",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ primary:
13
+ "px-6 py-3 green-action-button text-base font-semibold text-black tracking-tight",
14
+ secondary:
15
+ "px-6 py-3 blue-action-button text-base font-semibold text-black tracking-tight",
16
+ tertiary:
17
+ "px-3 py-2 grey-action-button text-sm font-semibold text-white",
18
+ grey_pure:
19
+ "px-3 py-2 border-button-grey text-sm font-semibold text-white bg-surface-300 hover:bg-action-hover active:bg-action-active",
20
+ },
21
+ fullWidth: {
22
+ true: "w-full",
23
+ false: "",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ variant: "primary",
28
+ },
29
+ },
30
+ );
31
+
32
+ export interface ButtonProps
33
+ extends
34
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
35
+ VariantProps<typeof buttonVariants> {
36
+ asChild?: boolean;
37
+ before?: React.ReactNode;
38
+ loading?: boolean;
39
+ noLoadingWhenDisabled?: boolean;
40
+ }
41
+
42
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
+ (
44
+ {
45
+ className,
46
+ variant,
47
+ fullWidth,
48
+ asChild = false,
49
+ disabled,
50
+ before,
51
+ children,
52
+ loading,
53
+ noLoadingWhenDisabled = false,
54
+ ...props
55
+ },
56
+ ref,
57
+ ) => {
58
+ return (
59
+ <button
60
+ className={cn(
61
+ "relative overflow-clip ease-in-out duration-300",
62
+ buttonVariants({ variant, fullWidth, className }),
63
+ )}
64
+ type="button"
65
+ ref={ref}
66
+ disabled={disabled || loading}
67
+ {...props}
68
+ >
69
+ {before}
70
+
71
+ <div className="inline-flex sm:flex-1 items-center justify-center">
72
+ {children}
73
+ </div>
74
+ </button>
75
+ );
76
+ },
77
+ );
78
+ Button.displayName = "Button";
79
+
80
+ export { Button, buttonVariants };
@@ -0,0 +1,61 @@
1
+ import { cn } from "../lib/utils";
2
+
3
+ interface CardScoreBadgeProps {
4
+ className?: string;
5
+ type:
6
+ | "default"
7
+ | "win"
8
+ | "lose"
9
+ | "bust"
10
+ | "blackjack"
11
+ | "push"
12
+ | "even"
13
+ | "surrender";
14
+ children?: React.ReactNode;
15
+ isMobileSmallHeight?: boolean;
16
+ }
17
+
18
+ const BADGE_COLORS = {
19
+ default: "bg-surface-300",
20
+ push: "bg-surface-300",
21
+ win: "bg-[#8BB339]",
22
+ lose: "bg-[#DB375E]",
23
+ bust: "bg-[#DB375E]",
24
+ blackjack: "bg-[#FF5C05]",
25
+ even: "bg-card-border",
26
+ surrender: "bg-[#8A8A8A]",
27
+ };
28
+
29
+ const CardScoreBadge = ({
30
+ type = "default",
31
+ className,
32
+ children,
33
+ isMobileSmallHeight = false,
34
+ }: CardScoreBadgeProps) => {
35
+ const badgeColor = BADGE_COLORS[type];
36
+
37
+ return (
38
+ <div
39
+ className={cn(
40
+ "pointer-events-none absolute bottom-1 right-0 z-10 select-none text-center",
41
+ className,
42
+ isMobileSmallHeight ? "h-4 min-w-4" : "h-5.5 min-w-5.5",
43
+ )}
44
+ >
45
+ <div
46
+ className={cn(
47
+ "relative w-full h-full flex items-center justify-center rounded font-bold",
48
+ badgeColor,
49
+ isMobileSmallHeight ? "p-0.5 text-[8px]" : "p-1 text-[10px]",
50
+ )}
51
+ style={{
52
+ boxShadow: "0 2px 0 0 #404040",
53
+ }}
54
+ >
55
+ {children}
56
+ </div>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default CardScoreBadge;
@@ -0,0 +1,52 @@
1
+ import { forwardRef } from "react";
2
+ import { PokerCard } from "./poker-card";
3
+ import cardShoeOverlay from "../assets/card-shoe-overlay.svg";
4
+ import { cn } from "../lib/utils";
5
+
6
+ interface CardShoeProps {
7
+ className?: string;
8
+ }
9
+
10
+ const CARD_COUNT = 4;
11
+ const CARD_OFFSET_Y = 2.5; // px vertical offset between stacked cards
12
+
13
+ export const CardShoe = forwardRef<HTMLDivElement, CardShoeProps>(
14
+ ({ className }, ref) => {
15
+ return (
16
+ <div
17
+ className={cn(
18
+ "relative w-12.5 h-28.5 border-[2.4px] rounded-t-[3.6px] rounded-b-[9.6px] overflow-hidden",
19
+ "bg-card-shoe-bg border-card-shoe-border shadow-[0px_2px_0px_0px_#122741]",
20
+ className,
21
+ )}
22
+ >
23
+ {/* Stacked card backs visible through the shoe opening */}
24
+ <div
25
+ ref={ref}
26
+ className="absolute bottom-[4px] left-1/2 -translate-x-1/2"
27
+ >
28
+ {Array.from({ length: CARD_COUNT }).map((_, i) => (
29
+ <div
30
+ key={i}
31
+ className="absolute left-1/2 -translate-x-1/2 select-none"
32
+ style={{
33
+ bottom: `${i * CARD_OFFSET_Y}px`,
34
+ }}
35
+ >
36
+ <PokerCard side="back" showShadow={false} />
37
+ </div>
38
+ ))}
39
+ </div>
40
+
41
+ {/* Shoe overlay with semicircular cutout */}
42
+ <img
43
+ src={cardShoeOverlay}
44
+ alt=""
45
+ className="absolute top-0 left-0 pointer-events-none w-12.5"
46
+ />
47
+ </div>
48
+ );
49
+ },
50
+ );
51
+
52
+ CardShoe.displayName = "CardShoe";
@@ -0,0 +1,182 @@
1
+ type DivProps = React.HTMLAttributes<HTMLDivElement>;
2
+
3
+ interface ICardTableProp {
4
+ className?: DivProps["className"];
5
+ }
6
+
7
+ const CardTable = ({ className }: ICardTableProp) => {
8
+ return (
9
+ <div className={className}>
10
+ <svg
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ width="1114"
13
+ height="521"
14
+ viewBox="0 0 1114 521"
15
+ fill="none"
16
+ >
17
+ <g filter="url(#filter0_din_6221_41932)">
18
+ <mask id="path-1-inside-1_6221_41932" fill="white">
19
+ <path d="M4 17C4 7.61116 11.6112 0 21 0H1093C1102.39 0 1110 7.61116 1110 17V113C1110 333.914 930.914 513 710 513H404C183.086 513 4 333.914 4 113V17Z" />
20
+ </mask>
21
+ <path
22
+ d="M4 17C4 7.61116 11.6112 0 21 0H1093C1102.39 0 1110 7.61116 1110 17V113C1110 333.914 930.914 513 710 513H404C183.086 513 4 333.914 4 113V17Z"
23
+ fill="url(#paint0_linear_6221_41932)"
24
+ />
25
+ <path
26
+ d="M4 17C4 7.61116 11.6112 0 21 0H1093C1102.39 0 1110 7.61116 1110 17V113C1110 333.914 930.914 513 710 513H404C183.086 513 4 333.914 4 113V17Z"
27
+ stroke="url(#paint1_linear_6221_41932)"
28
+ strokeWidth="50"
29
+ mask="url(#path-1-inside-1_6221_41932)"
30
+ />
31
+ </g>
32
+ <defs>
33
+ <filter
34
+ id="filter0_din_6221_41932"
35
+ x="0"
36
+ y="0"
37
+ width="1114"
38
+ height="521"
39
+ filterUnits="userSpaceOnUse"
40
+ colorInterpolationFilters="sRGB"
41
+ >
42
+ <feFlood floodOpacity="0" result="BackgroundImageFix" />
43
+ <feColorMatrix
44
+ in="SourceAlpha"
45
+ type="matrix"
46
+ values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
47
+ result="hardAlpha"
48
+ />
49
+ <feOffset dy="4" />
50
+ <feGaussianBlur stdDeviation="2" />
51
+ <feComposite in2="hardAlpha" operator="out" />
52
+ <feColorMatrix
53
+ type="matrix"
54
+ values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
55
+ />
56
+ <feBlend
57
+ mode="normal"
58
+ in2="BackgroundImageFix"
59
+ result="effect1_dropShadow_6221_41932"
60
+ />
61
+ <feBlend
62
+ mode="normal"
63
+ in="SourceGraphic"
64
+ in2="BackgroundImageFix"
65
+ result="shape"
66
+ />
67
+ <feColorMatrix
68
+ in="SourceAlpha"
69
+ type="matrix"
70
+ values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
71
+ result="hardAlpha"
72
+ />
73
+ <feMorphology
74
+ radius="35"
75
+ operator="erode"
76
+ in="SourceAlpha"
77
+ result="effect2_innerShadow_6221_41932"
78
+ />
79
+ <feOffset dy="4" />
80
+ <feGaussianBlur stdDeviation="5.45" />
81
+ <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
82
+ <feColorMatrix
83
+ type="matrix"
84
+ values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.35 0"
85
+ />
86
+ <feBlend
87
+ mode="normal"
88
+ in2="shape"
89
+ result="effect2_innerShadow_6221_41932"
90
+ />
91
+ <feTurbulence
92
+ type="fractalNoise"
93
+ baseFrequency="0.5 0.5"
94
+ stitchTiles="stitch"
95
+ numOctaves="3"
96
+ result="noise"
97
+ seed="9045"
98
+ />
99
+ <feColorMatrix
100
+ in="noise"
101
+ type="luminanceToAlpha"
102
+ result="alphaNoise"
103
+ />
104
+ <feComponentTransfer in="alphaNoise" result="coloredNoise1">
105
+ <feFuncA
106
+ type="discrete"
107
+ tableValues="1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
108
+ />
109
+ </feComponentTransfer>
110
+ <feComposite
111
+ operator="in"
112
+ in2="effect2_innerShadow_6221_41932"
113
+ in="coloredNoise1"
114
+ result="noise1Clipped"
115
+ />
116
+ <feComponentTransfer in="alphaNoise" result="coloredNoise2">
117
+ <feFuncA
118
+ type="discrete"
119
+ tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 "
120
+ />
121
+ </feComponentTransfer>
122
+ <feComposite
123
+ operator="in"
124
+ in2="effect2_innerShadow_6221_41932"
125
+ in="coloredNoise2"
126
+ result="noise2Clipped"
127
+ />
128
+ <feFlood floodColor="rgba(0, 0, 0, 0.05)" result="color1Flood" />
129
+ <feComposite
130
+ operator="in"
131
+ in2="noise1Clipped"
132
+ in="color1Flood"
133
+ result="color1"
134
+ />
135
+ <feFlood floodColor="rgba(255, 255, 255, 0)" result="color2Flood" />
136
+ <feComposite
137
+ operator="in"
138
+ in2="noise2Clipped"
139
+ in="color2Flood"
140
+ result="color2"
141
+ />
142
+ <feMerge result="effect3_noise_6221_41932">
143
+ <feMergeNode in="effect2_innerShadow_6221_41932" />
144
+ <feMergeNode in="color1" />
145
+ <feMergeNode in="color2" />
146
+ </feMerge>
147
+ <feBlend
148
+ mode="normal"
149
+ in="effect3_noise_6221_41932"
150
+ in2="effect1_dropShadow_6221_41932"
151
+ result="effect3_noise_6221_41932"
152
+ />
153
+ </filter>
154
+ <linearGradient
155
+ id="paint0_linear_6221_41932"
156
+ x1="557"
157
+ y1="0"
158
+ x2="557"
159
+ y2="513"
160
+ gradientUnits="userSpaceOnUse"
161
+ >
162
+ <stop stopColor="#0B3E2C" />
163
+ <stop offset="1" stopColor="#14945B" />
164
+ </linearGradient>
165
+ <linearGradient
166
+ id="paint1_linear_6221_41932"
167
+ x1="557"
168
+ y1="0"
169
+ x2="557"
170
+ y2="513"
171
+ gradientUnits="userSpaceOnUse"
172
+ >
173
+ <stop stopColor="#271709" />
174
+ <stop offset="1" stopColor="#311D0B" />
175
+ </linearGradient>
176
+ </defs>
177
+ </svg>
178
+ </div>
179
+ );
180
+ };
181
+
182
+ export default CardTable;