@jonelhatwell/arcade-games 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +87 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +34 -3
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +86 -30
- package/dist/index.js.map +1 -1
- package/package.json +57 -53
- package/components.json +0 -21
- package/eslint.config.js +0 -23
- package/postcss.config.mjs +0 -5
- package/public/vite.svg +0 -1
- package/src/components/games/spacedodger/index.tsx +0 -135
- package/src/components/ui/button.tsx +0 -62
- package/src/components/ui/card.tsx +0 -92
- package/src/index.css +0 -123
- package/src/index.ts +0 -3
- package/src/lib/utils.ts +0 -6
- package/src/types/globals.d.ts +0 -1
- package/tailwind.config.ts +0 -7
- package/tsconfig.json +0 -15
- package/tsup.config.ts +0 -17
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/components/games/spacedodger/index.tsx
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
|
|
1
4
|
// src/components/ui/button.tsx
|
|
2
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
6
|
import { cva } from "class-variance-authority";
|
|
@@ -58,33 +61,36 @@ function Button({
|
|
|
58
61
|
);
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
// src/components/games/test/index.tsx
|
|
62
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
63
|
-
var Test = () => {
|
|
64
|
-
return /* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2(Button, { variant: "outline", children: "Button" }) });
|
|
65
|
-
};
|
|
66
|
-
var test_default = Test;
|
|
67
|
-
|
|
68
64
|
// src/components/games/spacedodger/index.tsx
|
|
69
|
-
import {
|
|
70
|
-
|
|
65
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
66
|
+
var config = {
|
|
67
|
+
canvasSize: {
|
|
68
|
+
width: 450,
|
|
69
|
+
height: 450
|
|
70
|
+
},
|
|
71
|
+
player: { x: 200, y: 350, width: 25, height: 50 }
|
|
72
|
+
};
|
|
71
73
|
var SpaceDodger = () => {
|
|
72
74
|
const canvasRef = useRef(null);
|
|
75
|
+
const scoreRef = useRef(0);
|
|
76
|
+
const lastBatchScoreRef = useRef(0);
|
|
73
77
|
const [score, setScore] = useState(0);
|
|
74
78
|
const [gameOver, setGameOver] = useState(false);
|
|
75
79
|
const gameStateRef = useRef({
|
|
76
|
-
player:
|
|
80
|
+
player: config.player,
|
|
77
81
|
enemies: [],
|
|
78
82
|
frame: 0,
|
|
79
83
|
keys: {}
|
|
80
84
|
});
|
|
81
85
|
const resetGame = () => {
|
|
82
86
|
gameStateRef.current = {
|
|
83
|
-
player:
|
|
87
|
+
player: config.player,
|
|
84
88
|
enemies: [],
|
|
85
89
|
frame: 0,
|
|
86
90
|
keys: {}
|
|
87
91
|
};
|
|
92
|
+
scoreRef.current = 0;
|
|
93
|
+
lastBatchScoreRef.current = 0;
|
|
88
94
|
setScore(0);
|
|
89
95
|
setGameOver(false);
|
|
90
96
|
};
|
|
@@ -102,60 +108,110 @@ var SpaceDodger = () => {
|
|
|
102
108
|
};
|
|
103
109
|
window.addEventListener("keydown", handleKeyDown);
|
|
104
110
|
window.addEventListener("keyup", handleKeyUp);
|
|
111
|
+
const randomRadius = () => 10 + Math.random() * 15;
|
|
112
|
+
const getEnemySpeed = () => {
|
|
113
|
+
const base = 3;
|
|
114
|
+
const increase = Math.floor(scoreRef.current / 20) * 0.5;
|
|
115
|
+
return base + increase;
|
|
116
|
+
};
|
|
105
117
|
let animationId;
|
|
106
118
|
const gameLoop = () => {
|
|
107
119
|
if (gameOver) return;
|
|
108
120
|
state.frame++;
|
|
109
121
|
ctx.fillStyle = "#000";
|
|
110
|
-
ctx.fillRect(0, 0,
|
|
122
|
+
ctx.fillRect(0, 0, config.canvasSize.width, config.canvasSize.height);
|
|
111
123
|
if (state.keys["ArrowLeft"] && state.player.x > 0) state.player.x -= 5;
|
|
112
|
-
if (state.keys["ArrowRight"] && state.player.x <
|
|
124
|
+
if (state.keys["ArrowRight"] && state.player.x < config.canvasSize.width - state.player.width) state.player.x += 5;
|
|
113
125
|
if (state.keys["ArrowUp"] && state.player.y > 0) state.player.y -= 5;
|
|
114
|
-
if (state.keys["ArrowDown"] && state.player.y <
|
|
126
|
+
if (state.keys["ArrowDown"] && state.player.y < config.canvasSize.height - state.player.height) state.player.y += 5;
|
|
115
127
|
ctx.fillStyle = "#0ea5e9";
|
|
116
|
-
ctx.fillRect(
|
|
128
|
+
ctx.fillRect(
|
|
129
|
+
state.player.x,
|
|
130
|
+
state.player.y,
|
|
131
|
+
state.player.width,
|
|
132
|
+
state.player.height
|
|
133
|
+
);
|
|
117
134
|
if (state.frame % 40 === 0) {
|
|
118
|
-
|
|
135
|
+
const radius = randomRadius();
|
|
136
|
+
state.enemies.push({
|
|
137
|
+
x: radius + Math.random() * (config.canvasSize.width - radius * 2),
|
|
138
|
+
y: -radius,
|
|
139
|
+
radius
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (scoreRef.current > 0 && scoreRef.current % 20 === 0 && lastBatchScoreRef.current !== scoreRef.current) {
|
|
143
|
+
lastBatchScoreRef.current = scoreRef.current;
|
|
144
|
+
const batchCount = 2 + Math.floor(Math.random() * 3);
|
|
145
|
+
for (let i = 0; i < batchCount; i++) {
|
|
146
|
+
const radius = randomRadius();
|
|
147
|
+
state.enemies.push({
|
|
148
|
+
x: radius + Math.random() * (config.canvasSize.width - radius * 2),
|
|
149
|
+
y: -Math.random() * 100,
|
|
150
|
+
radius
|
|
151
|
+
});
|
|
152
|
+
}
|
|
119
153
|
}
|
|
120
154
|
state.enemies = state.enemies.filter((enemy) => {
|
|
121
|
-
enemy.y +=
|
|
155
|
+
enemy.y += getEnemySpeed();
|
|
122
156
|
ctx.fillStyle = "#ef4444";
|
|
123
|
-
ctx.
|
|
124
|
-
|
|
157
|
+
ctx.beginPath();
|
|
158
|
+
ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2);
|
|
159
|
+
ctx.fill();
|
|
160
|
+
const closestX = Math.max(
|
|
161
|
+
state.player.x,
|
|
162
|
+
Math.min(enemy.x, state.player.x + state.player.width)
|
|
163
|
+
);
|
|
164
|
+
const closestY = Math.max(
|
|
165
|
+
state.player.y,
|
|
166
|
+
Math.min(enemy.y, state.player.y + state.player.height)
|
|
167
|
+
);
|
|
168
|
+
const dx = enemy.x - closestX;
|
|
169
|
+
const dy = enemy.y - closestY;
|
|
170
|
+
if (dx * dx + dy * dy < enemy.radius * enemy.radius) {
|
|
125
171
|
setGameOver(true);
|
|
126
172
|
return false;
|
|
127
173
|
}
|
|
128
|
-
if (enemy.y >
|
|
129
|
-
setScore((s) =>
|
|
174
|
+
if (enemy.y - enemy.radius > config.canvasSize.height) {
|
|
175
|
+
setScore((s) => {
|
|
176
|
+
scoreRef.current = s + 1;
|
|
177
|
+
return s + 1;
|
|
178
|
+
});
|
|
130
179
|
return false;
|
|
131
180
|
}
|
|
132
181
|
return true;
|
|
133
182
|
});
|
|
134
183
|
animationId = requestAnimationFrame(gameLoop);
|
|
135
184
|
};
|
|
136
|
-
|
|
185
|
+
gameLoop();
|
|
137
186
|
return () => {
|
|
138
187
|
cancelAnimationFrame(animationId);
|
|
139
188
|
window.removeEventListener("keydown", handleKeyDown);
|
|
140
189
|
window.removeEventListener("keyup", handleKeyUp);
|
|
141
190
|
};
|
|
142
191
|
}, [gameOver]);
|
|
143
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 p-4", children: [
|
|
192
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center gap-4 p-4", children: [
|
|
144
193
|
/* @__PURE__ */ jsxs("div", { className: "text-xl font-bold", children: [
|
|
145
194
|
"Score: ",
|
|
146
195
|
score
|
|
147
196
|
] }),
|
|
148
|
-
/* @__PURE__ */
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
197
|
+
/* @__PURE__ */ jsx2(
|
|
198
|
+
"canvas",
|
|
199
|
+
{
|
|
200
|
+
ref: canvasRef,
|
|
201
|
+
width: config.canvasSize.width,
|
|
202
|
+
height: config.canvasSize.height,
|
|
203
|
+
className: "border-2 border-gray-300"
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
gameOver && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center bg-black/60", children: [
|
|
207
|
+
/* @__PURE__ */ jsx2("p", { className: "text-xl font-bold text-red-500 mb-3", children: "Game Over" }),
|
|
208
|
+
/* @__PURE__ */ jsx2(Button, { onClick: resetGame, children: "Play Again" })
|
|
152
209
|
] }),
|
|
153
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm text-foreground", children: "Use arrow keys to move" })
|
|
154
211
|
] });
|
|
155
212
|
};
|
|
156
213
|
var spacedodger_default = SpaceDodger;
|
|
157
214
|
export {
|
|
158
|
-
spacedodger_default as SpaceDodger
|
|
159
|
-
test_default as Test
|
|
215
|
+
spacedodger_default as SpaceDodger
|
|
160
216
|
};
|
|
161
217
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ui/button.tsx","../src/lib/utils.ts","../src/components/games/test/index.tsx","../src/components/games/spacedodger/index.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}","import { Button } from '@/components/ui/button'\r\n\r\nconst Test = () => {\r\n return (\r\n <div>\r\n <Button variant=\"outline\">Button</Button>\r\n </div>\r\n )\r\n}\r\n\r\nexport default Test","\r\nimport React, { useState, useEffect, useRef } from 'react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\n\r\ninterface Position {\r\n x: number;\r\n y: number;\r\n}\r\n\r\n\r\ninterface Enemy extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\ninterface Player extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\n\r\ninterface SpaceDodgerState {\r\n player: Player;\r\n enemies: Enemy[];\r\n frame: number;\r\n keys: Record<string, boolean>;\r\n}\r\n\r\nconst SpaceDodger: React.FC = () => {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const [score, setScore] = useState<number>(0);\r\n const [gameOver, setGameOver] = useState<boolean>(false);\r\n const gameStateRef = useRef<SpaceDodgerState>({\r\n player: { x: 200, y: 350, width: 30, height: 30 },\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n });\r\n\r\n const resetGame = (): void => {\r\n gameStateRef.current = {\r\n player: { x: 200, y: 350, width: 30, height: 30 },\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n };\r\n setScore(0);\r\n setGameOver(false);\r\n };\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n const state = gameStateRef.current;\r\n\r\n const handleKeyDown = (e: KeyboardEvent): void => { \r\n state.keys[e.key] = true; \r\n };\r\n const handleKeyUp = (e: KeyboardEvent): void => { \r\n state.keys[e.key] = false; \r\n };\r\n window.addEventListener('keydown', handleKeyDown);\r\n window.addEventListener('keyup', handleKeyUp);\r\n\r\n let animationId: number;\r\n const gameLoop = (): void => {\r\n if (gameOver) return;\r\n \r\n state.frame++;\r\n ctx.fillStyle = '#000';\r\n ctx.fillRect(0, 0, 400, 400);\r\n\r\n if (state.keys['ArrowLeft'] && state.player.x > 0) state.player.x -= 5;\r\n if (state.keys['ArrowRight'] && state.player.x < 370) state.player.x += 5;\r\n if (state.keys['ArrowUp'] && state.player.y > 0) state.player.y -= 5;\r\n if (state.keys['ArrowDown'] && state.player.y < 370) state.player.y += 5;\r\n\r\n ctx.fillStyle = '#0ea5e9';\r\n ctx.fillRect(state.player.x, state.player.y, state.player.width, state.player.height);\r\n\r\n if (state.frame % 40 === 0) {\r\n state.enemies.push({ x: Math.random() * 370, y: -20, width: 30, height: 30 });\r\n }\r\n\r\n state.enemies = state.enemies.filter(enemy => {\r\n enemy.y += 3;\r\n ctx.fillStyle = '#ef4444';\r\n ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);\r\n\r\n if (enemy.x < state.player.x + state.player.width &&\r\n enemy.x + enemy.width > state.player.x &&\r\n enemy.y < state.player.y + state.player.height &&\r\n enemy.y + enemy.height > state.player.y) {\r\n setGameOver(true);\r\n return false;\r\n }\r\n\r\n if (enemy.y > 400) {\r\n setScore(s => s + 1);\r\n return false;\r\n }\r\n return true;\r\n });\r\n\r\n animationId = requestAnimationFrame(gameLoop);\r\n };\r\n \r\n if (!gameOver) gameLoop();\r\n\r\n return () => {\r\n cancelAnimationFrame(animationId);\r\n window.removeEventListener('keydown', handleKeyDown);\r\n window.removeEventListener('keyup', handleKeyUp);\r\n };\r\n }, [gameOver]);\r\n\r\n return (\r\n <div className=\"flex flex-col items-center gap-4 p-4\">\r\n <div className=\"text-xl font-bold\">Score: {score}</div>\r\n <canvas ref={canvasRef} width=\"400\" height=\"400\" className=\"border-2 border-gray-300\" />\r\n {gameOver && (\r\n <div className=\"text-center\">\r\n <p className=\"text-xl font-bold text-red-500 mb-2\">Game Over!</p>\r\n <Button onClick={resetGame}>Play Again</Button>\r\n </div>\r\n )}\r\n <p className=\"text-sm text-gray-600\">Use arrow keys to move</p>\r\n </div>\r\n );\r\n};\r\n\r\nexport default SpaceDodger;"],"mappings":"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","names":["jsx","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/games/spacedodger/index.tsx","../src/components/ui/button.tsx","../src/lib/utils.ts"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\ninterface Position {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Enemy extends Position {\r\n radius: number;\r\n}\r\n\r\ninterface Player extends Position {\r\n width: number;\r\n height: number;\r\n}\r\n\r\ninterface SpaceDodgerState {\r\n player: Player;\r\n enemies: Enemy[];\r\n frame: number;\r\n keys: Record<string, boolean>;\r\n}\r\n\r\n\r\nconst config = {\r\n canvasSize : { \r\n width: 450, \r\n height: 450 \r\n },\r\n player: { x: 200, y: 350, width: 25, height: 50 }\r\n}\r\n\r\nconst SpaceDodger: React.FC = () => {\r\n const canvasRef = useRef<HTMLCanvasElement>(null);\r\n const scoreRef = useRef(0);\r\n const lastBatchScoreRef = useRef(0);\r\n\r\n const [score, setScore] = useState(0);\r\n const [gameOver, setGameOver] = useState(false);\r\n\r\n const gameStateRef = useRef<SpaceDodgerState>({\r\n player: config.player,\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n });\r\n\r\n const resetGame = () => {\r\n gameStateRef.current = {\r\n player: config.player,\r\n enemies: [],\r\n frame: 0,\r\n keys: {}\r\n };\r\n scoreRef.current = 0;\r\n lastBatchScoreRef.current = 0;\r\n setScore(0);\r\n setGameOver(false);\r\n };\r\n\r\n\r\n \r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n const state = gameStateRef.current;\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n state.keys[e.key] = true;\r\n };\r\n const handleKeyUp = (e: KeyboardEvent) => {\r\n state.keys[e.key] = false;\r\n };\r\n\r\n window.addEventListener('keydown', handleKeyDown);\r\n window.addEventListener('keyup', handleKeyUp);\r\n\r\n const randomRadius = () => 10 + Math.random() * 15;\r\n\r\n const getEnemySpeed = () => {\r\n const base = 3;\r\n const increase = Math.floor(scoreRef.current / 20) * 0.5;\r\n return base + increase;\r\n };\r\n\r\n let animationId: number;\r\n\r\n const gameLoop = () => {\r\n if (gameOver) return;\r\n\r\n state.frame++;\r\n\r\n ctx.fillStyle = '#000';\r\n ctx.fillRect(0, 0, config.canvasSize.width, config.canvasSize.height);\r\n\r\n // Player movement\r\n if (state.keys['ArrowLeft'] && state.player.x > 0) state.player.x -= 5;\r\n if (state.keys['ArrowRight'] && state.player.x < config.canvasSize.width - state.player.width) state.player.x += 5;\r\n if (state.keys['ArrowUp'] && state.player.y > 0) state.player.y -= 5;\r\n if (state.keys['ArrowDown'] && state.player.y < config.canvasSize.height - state.player.height) state.player.y += 5;\r\n\r\n // Draw player\r\n ctx.fillStyle = '#0ea5e9';\r\n ctx.fillRect(\r\n state.player.x,\r\n state.player.y,\r\n state.player.width,\r\n state.player.height\r\n );\r\n\r\n // Normal enemy spawn\r\n if (state.frame % 40 === 0) {\r\n const radius = randomRadius();\r\n state.enemies.push({\r\n x: radius + Math.random() * (config.canvasSize.width - radius * 2),\r\n y: -radius,\r\n radius,\r\n });\r\n }\r\n\r\n // Extra batch every 20 score\r\n if (\r\n scoreRef.current > 0 &&\r\n scoreRef.current % 20 === 0 &&\r\n lastBatchScoreRef.current !== scoreRef.current\r\n ) {\r\n lastBatchScoreRef.current = scoreRef.current;\r\n const batchCount = 2 + Math.floor(Math.random() * 3);\r\n\r\n for (let i = 0; i < batchCount; i++) {\r\n const radius = randomRadius();\r\n state.enemies.push({\r\n x: radius + Math.random() * (config.canvasSize.width - radius * 2),\r\n y: -Math.random() * 100,\r\n radius\r\n });\r\n }\r\n }\r\n\r\n // Enemies update\r\n state.enemies = state.enemies.filter(enemy => {\r\n enemy.y += getEnemySpeed();\r\n\r\n // Draw enemy (circle)\r\n ctx.fillStyle = '#ef4444';\r\n ctx.beginPath();\r\n ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2);\r\n ctx.fill();\r\n\r\n // Circle vs rectangle collision\r\n const closestX = Math.max(\r\n state.player.x,\r\n Math.min(enemy.x, state.player.x + state.player.width)\r\n );\r\n const closestY = Math.max(\r\n state.player.y,\r\n Math.min(enemy.y, state.player.y + state.player.height)\r\n );\r\n\r\n const dx = enemy.x - closestX;\r\n const dy = enemy.y - closestY;\r\n\r\n if (dx * dx + dy * dy < enemy.radius * enemy.radius) {\r\n setGameOver(true);\r\n return false;\r\n }\r\n\r\n // Passed screen\r\n if (enemy.y - enemy.radius > config.canvasSize.height) {\r\n setScore(s => {\r\n scoreRef.current = s + 1;\r\n return s + 1;\r\n });\r\n return false;\r\n }\r\n\r\n return true;\r\n });\r\n\r\n animationId = requestAnimationFrame(gameLoop);\r\n };\r\n\r\n gameLoop();\r\n\r\n return () => {\r\n cancelAnimationFrame(animationId);\r\n window.removeEventListener('keydown', handleKeyDown);\r\n window.removeEventListener('keyup', handleKeyUp);\r\n };\r\n }, [gameOver]);\r\n\r\n return (\r\n <div className=\"relative flex flex-col items-center gap-4 p-4\">\r\n <div className=\"text-xl font-bold\">Score: {score}</div>\r\n <canvas\r\n ref={canvasRef}\r\n width={config.canvasSize.width}\r\n height={config.canvasSize.height}\r\n className=\"border-2 border-gray-300\"\r\n />\r\n\r\n {gameOver && (\r\n <div className=\"absolute inset-0 flex flex-col items-center justify-center bg-black/60\">\r\n <p className=\"text-xl font-bold text-red-500 mb-3\">Game Over</p>\r\n <Button onClick={resetGame}>Play Again</Button>\r\n </div>\r\n )}\r\n\r\n <div className=\"text-sm text-foreground\">Use arrow keys to move</div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default SpaceDodger;","import * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\r\n outline:\r\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost:\r\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\r\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\r\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\r\n icon: \"size-9\",\r\n \"icon-sm\": \"size-8\",\r\n \"icon-lg\": \"size-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n }\r\n)\r\n\r\nfunction Button({\r\n className,\r\n variant = \"default\",\r\n size = \"default\",\r\n asChild = false,\r\n ...props\r\n}: React.ComponentProps<\"button\"> &\r\n VariantProps<typeof buttonVariants> & {\r\n asChild?: boolean\r\n }) {\r\n const Comp = asChild ? Slot : \"button\"\r\n\r\n return (\r\n <Comp\r\n data-slot=\"button\"\r\n data-variant={variant}\r\n data-size={size}\r\n className={cn(buttonVariants({ variant, size, className }))}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Button, buttonVariants }\r\n","import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}"],"mappings":"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","names":["jsx"]}
|
package/package.json
CHANGED
|
@@ -1,53 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@jonelhatwell/arcade-games",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"main": "dist/index.cjs",
|
|
6
|
-
"module": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"sideEffects": false,
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.js",
|
|
13
|
-
"require": "./dist/index.cjs"
|
|
14
|
-
},
|
|
15
|
-
"./styles": "./dist/index.css"
|
|
16
|
-
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"react": "^
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@jonelhatwell/arcade-games",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./styles": "./dist/index.css"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": "^18 || ^19",
|
|
27
|
+
"react-dom": "^18 || ^19"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
31
|
+
"class-variance-authority": "^0.7.1",
|
|
32
|
+
"clsx": "^2.1.1",
|
|
33
|
+
"lucide-react": "^0.562.0",
|
|
34
|
+
"react": "^19.2.0",
|
|
35
|
+
"react-dom": "^19.2.0",
|
|
36
|
+
"tailwind-merge": "^3.4.0",
|
|
37
|
+
"tw-animate-css": "^1.4.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@eslint/js": "^9.39.1",
|
|
41
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
42
|
+
"@types/node": "^24.10.1",
|
|
43
|
+
"@types/react": "^19.2.5",
|
|
44
|
+
"@types/react-dom": "^19.2.3",
|
|
45
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
46
|
+
"eslint": "^9.39.1",
|
|
47
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
48
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
49
|
+
"globals": "^16.5.0",
|
|
50
|
+
"postcss": "^8.5.6",
|
|
51
|
+
"tailwindcss": "^4.1.18",
|
|
52
|
+
"tsup": "^8.5.1",
|
|
53
|
+
"typescript": "~5.9.3",
|
|
54
|
+
"typescript-eslint": "^8.46.4",
|
|
55
|
+
"vite": "^7.2.4"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/components.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
-
"style": "new-york",
|
|
4
|
-
"rsc": false,
|
|
5
|
-
"tsx": true,
|
|
6
|
-
"tailwind": {
|
|
7
|
-
"config": "",
|
|
8
|
-
"css": "src/index.css",
|
|
9
|
-
"baseColor": "zinc",
|
|
10
|
-
"cssVariables": true,
|
|
11
|
-
"prefix": ""
|
|
12
|
-
},
|
|
13
|
-
"aliases": {
|
|
14
|
-
"components": "@/components",
|
|
15
|
-
"utils": "@/lib/utils",
|
|
16
|
-
"ui": "@/components/ui",
|
|
17
|
-
"lib": "@/lib",
|
|
18
|
-
"hooks": "@/hooks"
|
|
19
|
-
},
|
|
20
|
-
"iconLibrary": "lucide"
|
|
21
|
-
}
|
package/eslint.config.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import js from '@eslint/js'
|
|
2
|
-
import globals from 'globals'
|
|
3
|
-
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
-
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
-
import tseslint from 'typescript-eslint'
|
|
6
|
-
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
7
|
-
|
|
8
|
-
export default defineConfig([
|
|
9
|
-
globalIgnores(['dist']),
|
|
10
|
-
{
|
|
11
|
-
files: ['**/*.{ts,tsx}'],
|
|
12
|
-
extends: [
|
|
13
|
-
js.configs.recommended,
|
|
14
|
-
tseslint.configs.recommended,
|
|
15
|
-
reactHooks.configs.flat.recommended,
|
|
16
|
-
reactRefresh.configs.vite,
|
|
17
|
-
],
|
|
18
|
-
languageOptions: {
|
|
19
|
-
ecmaVersion: 2020,
|
|
20
|
-
globals: globals.browser,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
])
|
package/postcss.config.mjs
DELETED
package/public/vite.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
3
|
-
import { Button } from '@/components/ui/button';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface Position {
|
|
7
|
-
x: number;
|
|
8
|
-
y: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
interface Enemy extends Position {
|
|
13
|
-
width: number;
|
|
14
|
-
height: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface Player extends Position {
|
|
18
|
-
width: number;
|
|
19
|
-
height: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
interface SpaceDodgerState {
|
|
24
|
-
player: Player;
|
|
25
|
-
enemies: Enemy[];
|
|
26
|
-
frame: number;
|
|
27
|
-
keys: Record<string, boolean>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const SpaceDodger: React.FC = () => {
|
|
31
|
-
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
32
|
-
const [score, setScore] = useState<number>(0);
|
|
33
|
-
const [gameOver, setGameOver] = useState<boolean>(false);
|
|
34
|
-
const gameStateRef = useRef<SpaceDodgerState>({
|
|
35
|
-
player: { x: 200, y: 350, width: 30, height: 30 },
|
|
36
|
-
enemies: [],
|
|
37
|
-
frame: 0,
|
|
38
|
-
keys: {}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const resetGame = (): void => {
|
|
42
|
-
gameStateRef.current = {
|
|
43
|
-
player: { x: 200, y: 350, width: 30, height: 30 },
|
|
44
|
-
enemies: [],
|
|
45
|
-
frame: 0,
|
|
46
|
-
keys: {}
|
|
47
|
-
};
|
|
48
|
-
setScore(0);
|
|
49
|
-
setGameOver(false);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
const canvas = canvasRef.current;
|
|
54
|
-
if (!canvas) return;
|
|
55
|
-
const ctx = canvas.getContext('2d');
|
|
56
|
-
if (!ctx) return;
|
|
57
|
-
const state = gameStateRef.current;
|
|
58
|
-
|
|
59
|
-
const handleKeyDown = (e: KeyboardEvent): void => {
|
|
60
|
-
state.keys[e.key] = true;
|
|
61
|
-
};
|
|
62
|
-
const handleKeyUp = (e: KeyboardEvent): void => {
|
|
63
|
-
state.keys[e.key] = false;
|
|
64
|
-
};
|
|
65
|
-
window.addEventListener('keydown', handleKeyDown);
|
|
66
|
-
window.addEventListener('keyup', handleKeyUp);
|
|
67
|
-
|
|
68
|
-
let animationId: number;
|
|
69
|
-
const gameLoop = (): void => {
|
|
70
|
-
if (gameOver) return;
|
|
71
|
-
|
|
72
|
-
state.frame++;
|
|
73
|
-
ctx.fillStyle = '#000';
|
|
74
|
-
ctx.fillRect(0, 0, 400, 400);
|
|
75
|
-
|
|
76
|
-
if (state.keys['ArrowLeft'] && state.player.x > 0) state.player.x -= 5;
|
|
77
|
-
if (state.keys['ArrowRight'] && state.player.x < 370) state.player.x += 5;
|
|
78
|
-
if (state.keys['ArrowUp'] && state.player.y > 0) state.player.y -= 5;
|
|
79
|
-
if (state.keys['ArrowDown'] && state.player.y < 370) state.player.y += 5;
|
|
80
|
-
|
|
81
|
-
ctx.fillStyle = '#0ea5e9';
|
|
82
|
-
ctx.fillRect(state.player.x, state.player.y, state.player.width, state.player.height);
|
|
83
|
-
|
|
84
|
-
if (state.frame % 40 === 0) {
|
|
85
|
-
state.enemies.push({ x: Math.random() * 370, y: -20, width: 30, height: 30 });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
state.enemies = state.enemies.filter(enemy => {
|
|
89
|
-
enemy.y += 3;
|
|
90
|
-
ctx.fillStyle = '#ef4444';
|
|
91
|
-
ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
|
|
92
|
-
|
|
93
|
-
if (enemy.x < state.player.x + state.player.width &&
|
|
94
|
-
enemy.x + enemy.width > state.player.x &&
|
|
95
|
-
enemy.y < state.player.y + state.player.height &&
|
|
96
|
-
enemy.y + enemy.height > state.player.y) {
|
|
97
|
-
setGameOver(true);
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (enemy.y > 400) {
|
|
102
|
-
setScore(s => s + 1);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
animationId = requestAnimationFrame(gameLoop);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
if (!gameOver) gameLoop();
|
|
112
|
-
|
|
113
|
-
return () => {
|
|
114
|
-
cancelAnimationFrame(animationId);
|
|
115
|
-
window.removeEventListener('keydown', handleKeyDown);
|
|
116
|
-
window.removeEventListener('keyup', handleKeyUp);
|
|
117
|
-
};
|
|
118
|
-
}, [gameOver]);
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
<div className="flex flex-col items-center gap-4 p-4">
|
|
122
|
-
<div className="text-xl font-bold">Score: {score}</div>
|
|
123
|
-
<canvas ref={canvasRef} width="400" height="400" className="border-2 border-gray-300" />
|
|
124
|
-
{gameOver && (
|
|
125
|
-
<div className="text-center">
|
|
126
|
-
<p className="text-xl font-bold text-red-500 mb-2">Game Over!</p>
|
|
127
|
-
<Button onClick={resetGame}>Play Again</Button>
|
|
128
|
-
</div>
|
|
129
|
-
)}
|
|
130
|
-
<p className="text-sm text-gray-600">Use arrow keys to move</p>
|
|
131
|
-
</div>
|
|
132
|
-
);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
export default SpaceDodger;
|
|
@@ -1,62 +0,0 @@
|
|
|
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
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
-
destructive:
|
|
14
|
-
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
15
|
-
outline:
|
|
16
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
17
|
-
secondary:
|
|
18
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
-
ghost:
|
|
20
|
-
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
25
|
-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
26
|
-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
27
|
-
icon: "size-9",
|
|
28
|
-
"icon-sm": "size-8",
|
|
29
|
-
"icon-lg": "size-10",
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
defaultVariants: {
|
|
33
|
-
variant: "default",
|
|
34
|
-
size: "default",
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
function Button({
|
|
40
|
-
className,
|
|
41
|
-
variant = "default",
|
|
42
|
-
size = "default",
|
|
43
|
-
asChild = false,
|
|
44
|
-
...props
|
|
45
|
-
}: React.ComponentProps<"button"> &
|
|
46
|
-
VariantProps<typeof buttonVariants> & {
|
|
47
|
-
asChild?: boolean
|
|
48
|
-
}) {
|
|
49
|
-
const Comp = asChild ? Slot : "button"
|
|
50
|
-
|
|
51
|
-
return (
|
|
52
|
-
<Comp
|
|
53
|
-
data-slot="button"
|
|
54
|
-
data-variant={variant}
|
|
55
|
-
data-size={size}
|
|
56
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
57
|
-
{...props}
|
|
58
|
-
/>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export { Button, buttonVariants }
|