AAAA
This commit is contained in:
commit
a756a40572
27 changed files with 1152 additions and 0 deletions
18
.eslintrc.cjs
Normal file
18
.eslintrc.cjs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
2
.prettierrc.yml
Normal file
2
.prettierrc.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
semi: false
|
||||||
|
singleQuote: true
|
30
README.md
Normal file
30
README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
// other rules...
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
BIN
bun.lockb
Normal file
BIN
bun.lockb
Normal file
Binary file not shown.
17
components.json
Normal file
17
components.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "src/index.css",
|
||||||
|
"baseColor": "slate",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils"
|
||||||
|
}
|
||||||
|
}
|
13
index.html
Normal file
13
index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>1000 руб за мои страдания</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
40
package.json
Normal file
40
package.json
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"name": "darts",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bunx --bun vite",
|
||||||
|
"build": "tsc -b && bunx --bun vite build",
|
||||||
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "bunx --bun vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
|
"class-variance-authority": "^0.7.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"immer": "^10.1.1",
|
||||||
|
"lucide-react": "^0.396.0",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"tailwind-merge": "^2.3.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"zustand": "^4.5.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "^18.3.3",
|
||||||
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
||||||
|
"@typescript-eslint/parser": "^7.13.1",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
|
"tailwindcss": "^3.4.4",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.3.1"
|
||||||
|
}
|
||||||
|
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
|
@ -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>
|
After Width: | Height: | Size: 1.5 KiB |
3
src/App.css
Normal file
3
src/App.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
html, body, #root {
|
||||||
|
@apply h-full w-full;
|
||||||
|
}
|
87
src/App.tsx
Normal file
87
src/App.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { create } from 'zustand'
|
||||||
|
import './App.css'
|
||||||
|
import { produce } from 'immer'
|
||||||
|
import doska from './assets/doska.svg'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Button } from './components/ui/button'
|
||||||
|
import { Input } from './components/ui/input'
|
||||||
|
import { Label } from './components/ui/label'
|
||||||
|
|
||||||
|
interface Player {
|
||||||
|
points: number
|
||||||
|
throws: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Game {
|
||||||
|
players: Player[]
|
||||||
|
currentPlayerIdx: number
|
||||||
|
round: number
|
||||||
|
|
||||||
|
started: boolean
|
||||||
|
startedAt: number
|
||||||
|
|
||||||
|
start(nplayers: number): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useGame = create<Game>((set) => ({
|
||||||
|
players: [],
|
||||||
|
currentPlayerIdx: -1,
|
||||||
|
round: 0,
|
||||||
|
started: false,
|
||||||
|
startedAt: -1,
|
||||||
|
start(nplayers) {
|
||||||
|
set(state => produce(state, state => {
|
||||||
|
state.players = Array.from({ length: nplayers }, () => ({
|
||||||
|
points: 0,
|
||||||
|
throws: 0
|
||||||
|
}))
|
||||||
|
state.currentPlayerIdx = 0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [startedAt, started, round] = useGame(game => [game.startedAt, game.started, game.round])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col h-full w-full'>
|
||||||
|
{/* Верхняя часть: доска */}
|
||||||
|
<div className='flex flex-col h-full justify-start items-center'>
|
||||||
|
<div className="flex flex-row justify-between items-center w-full">
|
||||||
|
<div className="flex flex-row justify-start">
|
||||||
|
<Label htmlFor="round">Раунд</Label>
|
||||||
|
<p id="round">{started ? `#${round}` : "Игра не начата"}</p></div>
|
||||||
|
<p>{started ? new Date(Date.now() - startedAt).toLocaleTimeString() : ""}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Board />
|
||||||
|
</div>
|
||||||
|
{/* Нижняя часть: управление игрой */}
|
||||||
|
<Control />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Board() {
|
||||||
|
const [points, setPoints] = useState(0)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img src={doska} onMouseMove={e => {}} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Control() {
|
||||||
|
const [started, round, start, players] = useGame(game => [game.started, game.round, game.start, game.players])
|
||||||
|
const [nplayers, setNplayers] = useState(2)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex flex-col h-full justify-start gap-2'>
|
||||||
|
<Label htmlFor="nplayers">Количество игроков</Label>
|
||||||
|
<Input id="nplayers" onChange={ev => setNplayers(parseInt(ev.target.value))} type="number" value={nplayers} disabled={started} />
|
||||||
|
|
||||||
|
<Button onClick={() => start(nplayers)} disabled={started}>
|
||||||
|
{"Начать игру"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
444
src/assets/doska.svg
Normal file
444
src/assets/doska.svg
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
version="1.0"
|
||||||
|
width="453"
|
||||||
|
height="453"
|
||||||
|
id="svg5532"
|
||||||
|
sodipodi:docname="dartboard.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1060"
|
||||||
|
pagecolor="#505050"
|
||||||
|
bordercolor="#ffffff"
|
||||||
|
borderopacity="1"
|
||||||
|
inkscape:pageshadow="0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pagecheckerboard="1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.8122538"
|
||||||
|
inkscape:cx="350.39242"
|
||||||
|
inkscape:cy="147.60626"
|
||||||
|
inkscape:window-width="3440"
|
||||||
|
inkscape:window-height="1376"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg5532" />
|
||||||
|
<defs
|
||||||
|
id="defs5534">
|
||||||
|
<marker
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto"
|
||||||
|
id="Arrow2Send"
|
||||||
|
style="overflow:visible">
|
||||||
|
<path
|
||||||
|
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z"
|
||||||
|
transform="matrix(-0.3,0,0,-0.3,1.5,0)"
|
||||||
|
id="path9898"
|
||||||
|
style="font-size:12px;fill:#62adff;fill-opacity:1;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
transform="translate(-9.31263,-200.1479)"
|
||||||
|
id="layer1">
|
||||||
|
<g
|
||||||
|
transform="translate(235.81263,426.6479)"
|
||||||
|
id="Board">
|
||||||
|
<g
|
||||||
|
id="BaseBoard">
|
||||||
|
<path
|
||||||
|
d="M 226.5,0 A 226.5,226.5 0 1 1 -226.5,2.7737334e-14 A 226.5,226.5 0 1 1 226.5,-5.5474668e-14 z"
|
||||||
|
id="path1307"
|
||||||
|
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.10000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="Inside Spider"
|
||||||
|
style="stroke:#d0edfd;stroke-width:1.10000002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 1 1 -170.55,2.0885662e-14 A 170.55,170.55 0 1 1 170.55,-4.1771323e-14 z"
|
||||||
|
id="DoubleRed"
|
||||||
|
style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:evenodd" />
|
||||||
|
<path
|
||||||
|
d="M 146.91298,0 A 146.91298,146.91298 0 1 1 -146.91298,0 146.91298,146.91298 0 1 1 146.91298,0 Z"
|
||||||
|
id="path2195"
|
||||||
|
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:1.00096" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 1 1 -107.55,1.3170642e-14 A 107.55,107.55 0 1 1 107.55,-2.6341283e-14 z"
|
||||||
|
id="TrebleRed"
|
||||||
|
style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:evenodd" />
|
||||||
|
<path
|
||||||
|
d="M 85.734511,0 A 85.734511,85.734511 0 1 1 -85.734511,0 85.734511,85.734511 0 1 1 85.734511,0 Z"
|
||||||
|
id="path2197"
|
||||||
|
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-width:0.957928" />
|
||||||
|
<g
|
||||||
|
id="GreenDoubleSpiders"
|
||||||
|
style="opacity:1;fill:#00a000;fill-opacity:1;fill-rule:evenodd">
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(-0.45399,-0.891007,0.891007,-0.45399,0,0)"
|
||||||
|
id="path4826"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(-0.891006,-0.453991,0.453991,-0.891006,0,0)"
|
||||||
|
id="path4830"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(-0.987688,0.156434,-0.156434,-0.987688,0,0)"
|
||||||
|
id="path4832"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(-0.707107,0.707106,-0.707106,-0.707107,0,0)"
|
||||||
|
id="path4834"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(-0.156435,0.987688,-0.987688,-0.156435,0,0)"
|
||||||
|
id="path4836"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(0.45399,0.891007,-0.891007,0.45399,0,0)"
|
||||||
|
id="path4838"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(0.891006,0.453991,-0.453991,0.891006,0,0)"
|
||||||
|
id="path4840"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(0.987688,-0.156434,0.156434,0.987688,0,0)"
|
||||||
|
id="path4842"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(0.707107,-0.707106,0.707106,0.707107,0,0)"
|
||||||
|
id="path4844"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 170.55,0 A 170.55,170.55 0 0 1 162.20269,52.70285 L 0,0 z"
|
||||||
|
transform="matrix(0.156435,-0.987688,0.987688,0.156435,0,0)"
|
||||||
|
id="path4846"
|
||||||
|
style="fill:#00a000;fill-opacity:1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4876"
|
||||||
|
style="opacity:1;fill:#e7e4c7;fill-opacity:1;fill-rule:evenodd">
|
||||||
|
<path
|
||||||
|
d="m -66.614831,-130.81306 a 146.84024,146.84024 0 0 1 43.693212,-14.19675 L 0.04915736,0.02259513 Z"
|
||||||
|
id="path4878"
|
||||||
|
style="stroke-width:1.00046" />
|
||||||
|
<path
|
||||||
|
d="m -130.83239,-66.636197 a 146.87608,146.87608 0 0 1 27.0105,-37.176703 L 0.03510631,0.04423649 Z"
|
||||||
|
id="path4880"
|
||||||
|
style="stroke-width:1.0007" />
|
||||||
|
<path
|
||||||
|
d="m -145.08371,22.980164 a 146.90004,146.90004 3.5625082 0 1 2e-5,-45.960459 L 0.00775386,-5.8949186e-6 Z"
|
||||||
|
id="path4882"
|
||||||
|
style="stroke-width:1.00087" />
|
||||||
|
<path
|
||||||
|
d="M -103.71911,103.70974 A 146.731,146.731 84.345034 0 1 -130.70287,66.569709 L 0.03545567,-0.04467679 Z"
|
||||||
|
id="path4884"
|
||||||
|
style="stroke-width:0.999716" />
|
||||||
|
<path
|
||||||
|
d="M -22.935899,145.09705 A 146.92846,146.92846 3.5625082 0 1 -66.65534,130.89171 L 0.04886028,-0.02245864 Z"
|
||||||
|
id="path4886"
|
||||||
|
style="stroke-width:1.00106" />
|
||||||
|
<path
|
||||||
|
d="m 66.541322,130.66961 a 146.67951,146.67951 84.345034 0 1 -43.645387,14.1812 L -0.04969816,-0.02284371 Z"
|
||||||
|
id="path4888"
|
||||||
|
style="stroke-width:0.999365" />
|
||||||
|
<path
|
||||||
|
d="M 130.75061,66.594361 A 146.78454,146.78454 0 0 1 103.75694,103.7479 L -0.03532682,-0.04451434 Z"
|
||||||
|
id="path4890"
|
||||||
|
style="stroke-width:1.00008" />
|
||||||
|
<path
|
||||||
|
d="m 145.10415,-22.982174 a 146.91288,146.91288 0 0 1 -2e-5,45.964478 L 0,5.8734336e-6 Z"
|
||||||
|
id="path4892"
|
||||||
|
style="stroke-width:1.00095" />
|
||||||
|
<path
|
||||||
|
d="m 103.76307,-103.75374 a 146.79296,146.79296 0 0 1 26.99515,37.155715 L -0.03530643,0.04448874 Z"
|
||||||
|
id="path4894"
|
||||||
|
style="stroke-width:1.00014" />
|
||||||
|
<path
|
||||||
|
d="m 22.897236,-144.85771 a 146.68651,146.68651 0 0 1 43.647449,14.18195 L -0.04967436,0.02283283 Z"
|
||||||
|
id="path4896"
|
||||||
|
style="stroke-width:0.999413" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="GreenTrebleSpiders"
|
||||||
|
style="opacity:1;fill:#00a000;fill-opacity:1;fill-rule:evenodd">
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(-0.45399,-0.891007,0.891007,-0.45399,0,0)"
|
||||||
|
id="path4856" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(-0.891006,-0.453991,0.453991,-0.891006,0,0)"
|
||||||
|
id="path4858" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(-0.987688,0.156434,-0.156434,-0.987688,0,0)"
|
||||||
|
id="path4860" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(-0.707107,0.707106,-0.707106,-0.707107,0,0)"
|
||||||
|
id="path4862" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(-0.156435,0.987688,-0.987688,-0.156435,0,0)"
|
||||||
|
id="path4864" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(0.45399,0.891007,-0.891007,0.45399,0,0)"
|
||||||
|
id="path4866" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(0.891006,0.453991,-0.453991,0.891006,0,0)"
|
||||||
|
id="path4868" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(0.987688,-0.156434,0.156434,0.987688,0,0)"
|
||||||
|
id="path4870" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
id="path4872"
|
||||||
|
transform="matrix(0.707107,-0.707106,0.707106,0.707107,0,0)" />
|
||||||
|
<path
|
||||||
|
d="M 107.55,0 A 107.55,107.55 0 0 1 102.28613,33.234779 L 0,0 z"
|
||||||
|
transform="matrix(0.156435,-0.987688,0.987688,0.156435,0,0)"
|
||||||
|
id="path4874" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g5771"
|
||||||
|
style="opacity:1;fill:#e7e4c7;fill-opacity:1;fill-rule:evenodd">
|
||||||
|
<path
|
||||||
|
d="m -38.833913,-76.321755 a 85.694189,85.694189 0 0 1 25.498829,-8.285053 L 0.07038427,0.03235206 Z"
|
||||||
|
id="path5773"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.957477" />
|
||||||
|
<path
|
||||||
|
d="m -76.229063,-38.802647 a 85.610831,85.610831 82.981878 0 1 15.743825,-21.669485 L 0.05071872,0.06390925 Z"
|
||||||
|
id="path5775"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.956546" />
|
||||||
|
<path
|
||||||
|
d="m -84.815746,13.435193 a 85.884098,85.884098 88.977296 0 1 1.1e-5,-26.870466 L 0.01098178,-5.1174552e-6 Z"
|
||||||
|
id="path5777"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.959599" />
|
||||||
|
<path
|
||||||
|
d="M -60.542317,60.529123 A 85.690985,85.690985 0 0 1 -76.300853,38.839328 L 0.05040196,-0.06351024 Z"
|
||||||
|
id="path5779"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.957441" />
|
||||||
|
<path
|
||||||
|
d="M -13.335505,84.608722 A 85.696138,85.696138 1.7881672 0 1 -38.834904,76.323448 L 0.07037326,-0.03234708 Z"
|
||||||
|
id="path5781"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.957499" />
|
||||||
|
<path
|
||||||
|
d="M 38.823169,76.300864 A 85.670808,85.670808 0 0 1 13.331297,84.583656 L -0.07051328,-0.03241135 Z"
|
||||||
|
id="path5783"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.957216" />
|
||||||
|
<path
|
||||||
|
d="M 76.275705,38.826567 A 85.662947,85.662947 28.154966 0 1 60.522296,60.509243 L -0.05051285,-0.06364984 Z"
|
||||||
|
id="path5785"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.957128" />
|
||||||
|
<path
|
||||||
|
d="m 84.274967,-13.349618 a 85.337062,85.337062 0 0 1 -1.1e-5,26.699316 L -0.01145986,5.3402359e-6 Z"
|
||||||
|
id="path5787"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.953487" />
|
||||||
|
<path
|
||||||
|
d="m 60.4633,-60.449992 a 85.579859,85.579859 22.5 0 1 15.738099,21.661667 L -0.05084094,0.06406339 Z"
|
||||||
|
id="path5789"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.9562" />
|
||||||
|
<path
|
||||||
|
d="m 13.276586,-84.248473 a 85.332333,85.332333 0 0 1 25.391147,8.2501 L -0.07238066,0.03326978 Z"
|
||||||
|
id="path5791"
|
||||||
|
style="fill:#e7e4c7;fill-opacity:1;stroke-width:0.953434" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d="M 16.450001,0 A 16.450001,16.450001 0 1 1 -16.450001,2.0144775e-15 A 16.450001,16.450001 0 1 1 16.450001,-4.0289551e-15 z"
|
||||||
|
id="path5939"
|
||||||
|
style="opacity:1;fill:#00a000;fill-opacity:1;fill-rule:evenodd" />
|
||||||
|
<path
|
||||||
|
d="M 6.9000001,0 A 6.9000001,6.9000001 0 1 1 -6.9000001,8.449784e-16 A 6.9000001,6.9000001 0 1 1 6.9000001,-1.6899568e-15 z"
|
||||||
|
id="path5944"
|
||||||
|
style="opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:evenodd" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="BoardNumbers"
|
||||||
|
style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;font-family:Sans">
|
||||||
|
<text
|
||||||
|
x="-16.95171"
|
||||||
|
y="-187.82498"
|
||||||
|
id="text6007"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-16.95171"
|
||||||
|
y="-187.82498"
|
||||||
|
id="tspan6009">20</tspan></text>
|
||||||
|
<text
|
||||||
|
x="51.872234"
|
||||||
|
y="-180.36624"
|
||||||
|
id="text6895"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="51.872234"
|
||||||
|
y="-180.36624"
|
||||||
|
id="tspan6897">1</tspan></text>
|
||||||
|
<text
|
||||||
|
x="100.0151"
|
||||||
|
y="-150.53122"
|
||||||
|
id="text6903"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="100.0151"
|
||||||
|
y="-150.53122"
|
||||||
|
id="tspan6907">18</tspan></text>
|
||||||
|
<text
|
||||||
|
x="153.24347"
|
||||||
|
y="-103.74449"
|
||||||
|
id="text6911"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="153.24347"
|
||||||
|
y="-103.74449"
|
||||||
|
id="tspan6913">4</tspan></text>
|
||||||
|
<text
|
||||||
|
x="173.92455"
|
||||||
|
y="-45.769642"
|
||||||
|
id="text6915"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="173.92455"
|
||||||
|
y="-45.769642"
|
||||||
|
id="tspan6917">13</tspan></text>
|
||||||
|
<text
|
||||||
|
x="187.14688"
|
||||||
|
y="9.1538963"
|
||||||
|
id="text6966"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="187.14688"
|
||||||
|
y="9.1538963"
|
||||||
|
id="tspan6968">6</tspan><tspan
|
||||||
|
x="187.14688"
|
||||||
|
y="37.153896"
|
||||||
|
id="tspan6970" /></text>
|
||||||
|
<text
|
||||||
|
x="171.5513"
|
||||||
|
y="67.467781"
|
||||||
|
id="text6923"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="171.5513"
|
||||||
|
y="67.467781"
|
||||||
|
id="tspan6925">10</tspan></text>
|
||||||
|
<text
|
||||||
|
x="142.7334"
|
||||||
|
y="127.13781"
|
||||||
|
id="text6927"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="142.7334"
|
||||||
|
y="127.13781"
|
||||||
|
id="tspan6929">15</tspan></text>
|
||||||
|
<text
|
||||||
|
x="106.11771"
|
||||||
|
y="169.85611"
|
||||||
|
id="text6931"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="106.11771"
|
||||||
|
y="169.85611"
|
||||||
|
id="tspan6933">2</tspan></text>
|
||||||
|
<text
|
||||||
|
x="42.718311"
|
||||||
|
y="196.63982"
|
||||||
|
id="text6975"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="42.718311"
|
||||||
|
y="196.63982"
|
||||||
|
id="tspan6977">17</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-6.1026163"
|
||||||
|
y="208.16699"
|
||||||
|
id="text6979"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-6.1026163"
|
||||||
|
y="208.16699"
|
||||||
|
id="tspan6981">3</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-74.248497"
|
||||||
|
y="199.01306"
|
||||||
|
id="text6983"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-74.248497"
|
||||||
|
y="199.01306"
|
||||||
|
id="tspan6985">19</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-123.74748"
|
||||||
|
y="173.24644"
|
||||||
|
id="text6987"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-123.74748"
|
||||||
|
y="173.24644"
|
||||||
|
id="tspan6989">7</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-174.94165"
|
||||||
|
y="127.13781"
|
||||||
|
id="text6991"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-174.94165"
|
||||||
|
y="127.13781"
|
||||||
|
id="tspan6993">16</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-193.58855"
|
||||||
|
y="72.892326"
|
||||||
|
id="text6995"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-193.58855"
|
||||||
|
y="72.892326"
|
||||||
|
id="tspan6997">8</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-214.60867"
|
||||||
|
y="8.8148642"
|
||||||
|
id="text7011"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-214.60867"
|
||||||
|
y="8.8148642"
|
||||||
|
id="tspan7013">11</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-208.50606"
|
||||||
|
y="-48.820953"
|
||||||
|
id="text7015"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-208.50606"
|
||||||
|
y="-48.820953"
|
||||||
|
id="tspan7017">14</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-165.10965"
|
||||||
|
y="-109.50809"
|
||||||
|
id="text7019"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-165.10965"
|
||||||
|
y="-109.50809"
|
||||||
|
id="tspan7021">9</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-134.25755"
|
||||||
|
y="-152.22639"
|
||||||
|
id="text7023"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-134.25755"
|
||||||
|
y="-152.22639"
|
||||||
|
id="tspan7025">12</tspan></text>
|
||||||
|
<text
|
||||||
|
x="-67.467812"
|
||||||
|
y="-178.67107"
|
||||||
|
id="text7027"
|
||||||
|
xml:space="preserve"><tspan
|
||||||
|
x="-67.467812"
|
||||||
|
y="-178.67107"
|
||||||
|
id="tspan7029">5</tspan></text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 18 KiB |
36
src/components/ui/badge.tsx
Normal file
36
src/components/ui/badge.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||||
|
secondary:
|
||||||
|
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
destructive:
|
||||||
|
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||||
|
outline: "text-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants }
|
56
src/components/ui/button.tsx
Normal file
56
src/components/ui/button.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Button.displayName = "Button"
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
79
src/components/ui/card.tsx
Normal file
79
src/components/ui/card.tsx
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Card.displayName = "Card"
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardHeader.displayName = "CardHeader"
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h3
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-2xl font-semibold leading-none tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardTitle.displayName = "CardTitle"
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardDescription.displayName = "CardDescription"
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
|
))
|
||||||
|
CardContent.displayName = "CardContent"
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardFooter.displayName = "CardFooter"
|
||||||
|
|
||||||
|
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
25
src/components/ui/input.tsx
Normal file
25
src/components/ui/input.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export interface InputProps
|
||||||
|
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||||
|
|
||||||
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Input.displayName = "Input"
|
||||||
|
|
||||||
|
export { Input }
|
24
src/components/ui/label.tsx
Normal file
24
src/components/ui/label.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
76
src/index.css
Normal file
76
src/index.css
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--primary: 222.2 47.4% 11.2%;
|
||||||
|
--primary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--secondary: 210 40% 96.1%;
|
||||||
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
--input: 214.3 31.8% 91.4%;
|
||||||
|
--ring: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 222.2 84% 4.9%;
|
||||||
|
--foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--card: 222.2 84% 4.9%;
|
||||||
|
--card-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--popover: 222.2 84% 4.9%;
|
||||||
|
--popover-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--primary: 210 40% 98%;
|
||||||
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--muted: 217.2 32.6% 17.5%;
|
||||||
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
|
|
||||||
|
--accent: 217.2 32.6% 17.5%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--border: 217.2 32.6% 17.5%;
|
||||||
|
--input: 217.2 32.6% 17.5%;
|
||||||
|
--ring: 212.7 26.8% 83.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { type ClassValue, clsx } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/// <reference types="vite/client" />
|
80
tailwind.config.ts
Normal file
80
tailwind.config.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import type { Config } from "tailwindcss"
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
'./pages/**/*.{ts,tsx}',
|
||||||
|
'./components/**/*.{ts,tsx}',
|
||||||
|
'./app/**/*.{ts,tsx}',
|
||||||
|
'./src/**/*.{ts,tsx}',
|
||||||
|
],
|
||||||
|
prefix: "",
|
||||||
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: "hsl(var(--border))",
|
||||||
|
input: "hsl(var(--input))",
|
||||||
|
ring: "hsl(var(--ring))",
|
||||||
|
background: "hsl(var(--background))",
|
||||||
|
foreground: "hsl(var(--foreground))",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary))",
|
||||||
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
|
foreground: "hsl(var(--secondary-foreground))",
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted))",
|
||||||
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent))",
|
||||||
|
foreground: "hsl(var(--accent-foreground))",
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover))",
|
||||||
|
foreground: "hsl(var(--popover-foreground))",
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card))",
|
||||||
|
foreground: "hsl(var(--card-foreground))",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)",
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
"accordion-down": {
|
||||||
|
from: { height: "0" },
|
||||||
|
to: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
},
|
||||||
|
"accordion-up": {
|
||||||
|
from: { height: "var(--radix-accordion-content-height)" },
|
||||||
|
to: { height: "0" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [require("tailwindcss-animate")],
|
||||||
|
} satisfies Config
|
||||||
|
|
||||||
|
export default config
|
27
tsconfig.app.json
Normal file
27
tsconfig.app.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
tsconfig.node.json
Normal file
13
tsconfig.node.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
15
vite.config.ts
Normal file
15
vite.config.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react-swc'
|
||||||
|
// @ts-ignore
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
// @ts-ignore
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in a new issue