From 6bad98efd19220de26db36164dfcb5ac8a042008 Mon Sep 17 00:00:00 2001 From: ilya Date: Mon, 24 Jun 2024 12:30:01 +0300 Subject: [PATCH] sanity --- .prettierrc.yml | 2 +- bun.lockb | Bin 113231 -> 113561 bytes components.json | 2 +- package.json | 4 +- src/App.css | 6 +- src/App.tsx | 324 +++++++++++++++++++++++++++++++---- src/components/ui/badge.tsx | 20 +-- src/components/ui/button.tsx | 42 ++--- src/components/ui/card.tsx | 32 ++-- src/components/ui/input.tsx | 12 +- src/components/ui/label.tsx | 10 +- src/index.css | 126 +++++++------- src/lib/math.ts | 77 +++++++++ src/lib/utils.ts | 4 +- src/main.tsx | 2 +- tailwind.config.ts | 76 ++++---- tsconfig.json | 8 +- vite.config.ts | 2 +- 18 files changed, 540 insertions(+), 209 deletions(-) create mode 100644 src/lib/math.ts diff --git a/.prettierrc.yml b/.prettierrc.yml index b0ae380..1d2127c 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -1,2 +1,2 @@ semi: false -singleQuote: true \ No newline at end of file +singleQuote: true diff --git a/bun.lockb b/bun.lockb index 29ff9f8f0fd6e49330840c617c7c84a7ac8b8ee9..7c5f74a547ae6d28764e7da1cb11244c57e6d9dd 100644 GIT binary patch delta 13226 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 delta 13197 zcmeHNdwfl2*52>okR2jI$mJvwA|laTj)aqlqyzYhNVo*KdAbXXd}J@9#YOS!?aJ*M8S~ zJNxYOuK2Opxx3BY3G$CQeD?hXSBneh&(G}|oL7GCY!CCim3|d&QTH>F6Gx16JW#;| zD!xl?UIwR5B{Od|;S-vA=RWck1P?*TJA`xXz&E-Gf*bh7oEh2VKmEB$lU|rA2&er8 z!4vi!VDjghJ{wH_Bf%ZO;o#0-SFktuVOQ1uDcA@4E^sICamZ3eZc_ zJAmm%O~5AbeLq2P0B1ix=7pR|Zva#5LXD3ORO?#_rfiqMPBmKyrab0o>^4ZP|D@~}r%*nQ(CPf6 zc!k~bn!!K;#${(u4a|97n5JDgfs1xt;>N*h*j3zY=i;_1LDjOKpY}rb^jX5#30ae# zMbHGEW#^T9d#GCVFTqrmKY^*rl^XX`LS7(vc_fmL#O6x2d1t_MI=}7u_U!d z_mb5n`Ap+#Fb$urU^nm*Fio%*RXsUx6a)$|08D)xtm!5&-M|S(Vf-DT||51&ZsL3+5|kkqs7g7peafn=jA#A-;9 zN^<6o7S@6ng3Tr25@`V(T5Q#S`Cxgi(gRGNZ4K^V(=D3+4V+tMNS6pXof%CDPazEt5);SO6&r z-c5N4yp7yH#>$d;PK;GNh#;6H7!P$Oi@_<1Z+3}h68Dd_imRg3TL|3jVi7MxL)#&Q zImjZ~M+*YFl7^9sh4tkAeXPb@*oN}k&XM9-NHnbAi{aP-3m^@-W=fo?(5T`~c%8FF z+zJhCNg9UZ7tm+`Hs^KT7Gt*1PJ%-ybxgyG)h*TU)y=kK&#=q zIKFvcH0#D|2U^7m{Zw}&uj^o8)jTKOYN+qWH^)bdsr}V!aR)DpxCokB7s{au8Z|dM z3g?^#2m;m(>Y8>I<78<4`DEuvW2qvcRm2C7P!-e)ea@nIkz^I;qK7D-=6qTQi&z6q z9bJ@NORVUW1=;~xe`wU2g3@9OpphpuXA3*X{S&OB{~)!)4b(51S{2CL4>;1$Pf8m0u! z?Sw|vRL9_Vnualmb3qAe6xDM)G<85wCvAkLrU%XNRRW)w7%iGGD73-v9&QJPnh&EC zBX|uos*&3DpF$hKi={|5hZiMT#YgytLhm4X%oDK>dP%L*G|ZYjo|9~4$9NHrgL!ST z)wmfmF`O5>L>j(KQet=6!};THAy@mHQH!OQP#lJNaOS;(g{dI zc_3`Asmj!cG#L^WW=JQW*t~`-%QjsLDN#9l_=&B(O%Noqu`FI|vx)`KX^j>5G-r$9 zqK!{Xi#9G#6NFKGa$2NONEd_@MM{B$v6dDo7DA%Ygnq?{xe1NNeREzSSws`Q?6556 zwNU0n1~h6}wL=#})4CVUbrxDLoKw5dE<|gQDyS)2oQGy+5@Pk;*T0Cr7_ce(9uE@ zAXX42Dh?3-&rB7;Dxy>vvsDR%xuTTDOclbEP%4HYE(km-(*>glPfm0|Y0Tt}0jhXo zU@C=}?xtL%G3m(w6=KGVGhK2?!yr*gV>ZHqK2QoV-39HhL>;B+qrp^&DS8$_eq#VC zjoA*m+F~k`-?R96qFgYEyitfL&=jCK@B%=kF;nZ$1Sr5Pjc0?Y{1!KZJrAI`uL4vW zSjk^CP{8>B@dAJfF0p*8vn~y=EsS{R2(^HRqCnF5Cc6z)b*^-@|l+B7k@^ zK!up%76WA8rtx+#6^(iD)9rG}uN0vB?*eG-?a}xExCL+$Xajr>Q28&iItKoofG()W z4@!0Z6V}Gye;!D!$?uLs_optp3v>b8N%<91*LY}lV(Kz4O(!PN8wc|1q}ho{^wISH zjd}Q(mdY6L(=O-=rdbxGUHDr}Hww|t6O(AxxVvU=%p%T5YIb50duuw_U8#ArW*{cf zN(X*>jF(cqzM3_TtSH3voiI?d$7}X~hbbLNyPlZD!I};p1XVRqyCi8Ri0Q%%`;k&>A!d%VqzsVGGo)(vw8gT)bzLeeqQw82;+B|$427H7ibkLYCcu&)b zNnEb!jhO-!z)maH2Vh#sHfdZ8#z!f?`RJn0LwaVRLQDa@0V>|gQDq9$5g_ga{LW_= zT%kO}{DSEh)-Mc=b*e~Tfco^0XO};oUH*7>5&n2~`2lOTQhxU{hkDt6{_v!)nSVUH z&@&4aViNy&cKQGG>@r&EdpyLbo&R5db}6lxvM+$2Ss}5x+^x{YT?-xf+(L=v@)OXG zLknCfu~+!4l{P+er31eTZ9ey3W#e5}Iq=1+B)qk~0_`%isMQjCoiAE#WBL3#j*EE2 z`!@ClUy9?KycWl|cxKU`2!sBPu)5j3Qvl|%E~F0s8lXS4%ZuVdMwxM=l;gen*hs5s5wE70}9| zc~(p8A3UcT`Bfu7Xg_hcqsZ?l@;fRSeir!2qr>@esDZ~M#`vsbNbndEgw}-nA4h`6 zk>GI&fB#&8b{SgK35hl5i%uZH6G#x6$RkeLm_1*LV+&r3V@n=;%Elb{3LIPUIvgE& z{Ar|i8mXO@m=k|++Q!=O#4|S5mKU8tVrP)pS&6md>1UDHStJI{g^TBq*f}J2PGWAn z0$MpV&l-uDcuoxxt3hJWJh|I>Bz7K&otKywKLPDHw7?4z>&RzaKwcM+7c?L4kJr&% zFFKSizL;yzy`7tS>U$*;{M5fx>Boq^Z(?U@?6qf@K?o_`f9V3_HJ6+6yz3#XOKHh< zo56U!5B?A5RhLdt9RB$YJL5k59V3=jUAkycOb2h;8K1@L($;XYk_A)9=~mm8`oGU( zC*=h3X_u^}!*0!G$`#+$+8O_hm){P!;vc2;-+X1UPt#s`eBvXo9m$;Kr+ipf^~vb* z&uXFN*`zJbj_O+dM5kiF?5WwjG#mX` z&8J_p6xdrc(r@*tnqx=JMn8YMK&GOae&zJXr`#?SLP_Fc4CT5VY__E+!M|iXboPah!TfiA;54ZrXfEy5vD`S98 zfDh0az>pTY0@T~oyWN1NfIuJ^Fa!86MF<7LfF1z#xCJ->RMBOJAkdrB9e@Y08Q28S zdj{&+g}`gTRJo)J>N^2q4lohu2lNL90Q5pB8K9RxnLrE>3(!lU;eZXG=gJ`nOE1xA z#3hgpBmzrde;-%_tOe*zm<8wsjD(&EU{JMDhVK9<1IY=PB2V*UCi57GV}Wd70x%rF zOrw9fg`q$kK>x^u0};UIa-|>3FrI{XTK4S9thx06PZ2;5K<@y{0D1*Q@8~$}qrmjS z%Nw9+upYK`0A2wKQNYvW1JE${1!#H0o*^&w@ce zz?(oSK<_|TQ1uI;ECsd$`7n+HrvZ_`s{l=InujFk12n(rZ;JxpJ%HvG%_Y(abnX__ z1Z8coGL@o8HUiX2)QatZ=D=gvXbAoU(ES|&dw^d4&j(%sa?3me*nGn)wl(Hbmo4aS$T2#H1zS*R&!MW&UR)&W|j_4cRlX!`$y2Eh=3 zR&Cvvnt|kPU|RX;dpQI$HK!S%?{4~D*8R!;9e@}%WmI74Qw9L7$okFc#x#AXQgk!& z(JMrOXrMH%Bu%@TV4OgCIYZtM!UD=Z31OpIbDBgba{)u>Mh+XBbu_1pzh|N9$3xgOk8Tx zcdI5v*L3snblTY9Fi&0z2llSKh5Yp0t|5I2N0&8oG&K0_k^cgRp8EFKL&BW_U1|+6 z4GuT7I^~mbN&ef3lHjHp+PuDPKE=%3SO63f=S=?8tiB{#%pQmHwHE;B*+{ThHp*zTl{|M zp>La=@*tuyo{*mrN;u10qu$u0_bx2ea-T`rhI0lS+R$d2Yfp@aTyUSUMg@$Oi{A*B<&V>DP))rO?GFkGA`pBsk$S>0FNy1S)g z+WLnJf9~9H)n?f<5w!}BQ}@jNt);hHi&uTS!+}~CVQ?mzo;KL*G&zzC3VQ$HQ&V1M@=Efui5(tz_J3)nK)y zhbNvIlGXa-V+cf}1=ZUqS0$ruSILekN-h0U&_4-sW(w;knun^pt19I!IO(D9sSbU2 z?Ch%5Yv&r+2{<^QmA)J*f04pm4Y!BN4^!B9)Bp6eOVsvT%X3mO!1V3cWvi@>xg{b$S8&d8UjAUYHCu-WA}q&bm*J?QQO3(YH^Zchh%PUX`SDcnK1{^#Dcz4`^q=@vd_Dl&NQ4EC7;2iCT(wb zUTVwQh1uz|2Q)Y=kr{5zw#p`uM|HCDmua3~?Wr^K+a|ox;P+XwJj{lvsP88Kc;$gl zKmYoJ!Uhk=6gAIN!OsP!c)I;T$M>2Q&((set) => ({ @@ -29,59 +53,287 @@ const useGame = create((set) => ({ 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 - })) - } + maxThrows: 3, + o1: 501, + start(nplayers, mthrows, o1) { + console.log('start', nplayers) + set((state) => + produce(state, (state) => { + state.o1 = o1 + state.round = 1 + state.started = true + state.startedAt = new Date().getTime() + state.maxThrows = mthrows + state.players = Array.from({ length: nplayers }, () => ({ + pts: 0, + throws: [], + })) + state.currentPlayerIdx = 0 + }), + ) + }, + doThrow(pts, double): Throw { + let th: Throw = { type: 'real', pts } + set((state) => + produce(state, (state) => { + if ( + state.players[state.currentPlayerIdx].throws.length >= state.maxThrows + ) { + state.currentPlayerIdx = + (state.currentPlayerIdx + 1) % state.players.length + if (state.currentPlayerIdx === 0) { + state.round++ + state.players.forEach(p => p.throws = []) + } + } + const player = state.players[state.currentPlayerIdx] + if (player.pts + pts === state.o1 && double) th = { type: 'finish' } + else if (Math.abs(player.pts + pts - state.o1) <= 1) th = { type: 'bust', remaining: player.pts + pts - state.o1 } + player.throws.push(th) + if (th.type === 'real') player.pts += th.pts + }), + ) + return th + }, + finish() { + let idx: number = 0 + set((state) => { + state.players.forEach((player, ix) => { + console.log(player, ix) + if (player.pts > state.players[idx].pts) { + idx = ix + console.log('winner', player, ix) + } + }) + return produce(state, (state) => { + state.started = false + state.currentPlayerIdx = -1 + }) + }) + console.log('winner', idx) + return idx + }, })) +function fuckingGameTime(ms: number) { + const duration = ms / 1000 + const hours = Math.floor(duration / 3600) + const minutes = Math.floor((duration % 3600) / 60) + const seconds = Math.floor(duration % 60) + return `${hours < 10 ? '0' + hours : hours}:${minutes < 10 ? '0' + minutes : minutes}:${seconds < 10 ? '0' + seconds : seconds}` +} + +function useTimer(startedAt: number) { + const [time, setTime] = useState(0) + + useEffect(() => { + const interval = setInterval( + () => setTime(new Date().getTime() - startedAt), + 1000, + ) + + return () => clearInterval(interval) + }, [startedAt]) + + return time +} + export default function App() { - const [startedAt, started, round] = useGame(game => [game.startedAt, game.started, game.round]) + const [startedAt, started, round, doThrow, finish] = useGame((game) => [ + game.startedAt, + game.started, + game.round, + game.doThrow, + game.finish + ]) + const gameTime = useTimer(startedAt) + const [points, setPoints] = useState(0) + const [double, setDouble] = useState(false) + const [winner, setWinner] = useState(undefined) return ( -
+
{/* Верхняя часть: доска */} -
+
-
- -

{started ? `#${round}` : "Игра не начата"}

-

{started ? new Date(Date.now() - startedAt).toLocaleTimeString() : ""}

+
+ +

#{round}

+
+ {points > 0 ? ( + + ) : null} +
+ +

{started ? fuckingGameTime(gameTime) : '00:00:00'}

+
- +
{/* Нижняя часть: управление игрой */} - +
) } -function Board() { - const [points, setPoints] = useState(0) - +function Board({ + setPoints, + setDouble, +}: { + setPoints: (w: number) => void + setDouble: (d: boolean) => void +}) { return ( - {}} /> + { + const { points, double } = getPoints(e) + setPoints(points) + setDouble(double) + }} + /> ) } -function Control() { - const [started, round, start, players] = useGame(game => [game.started, game.round, game.start, game.players]) +function Control({ winner, setWinner }: {winner: number | undefined, setWinner: (w: number | undefined) => void}) { + const [started, start, players, finish] = useGame((game) => [ + game.started, + game.start, + game.players, + game.finish, + ]) const [nplayers, setNplayers] = useState(2) + const [mthrows, setMthrows] = useState(3) + const [o1, setO1] = useState(501) return ( -
- - setNplayers(parseInt(ev.target.value))} type="number" value={nplayers} disabled={started} /> +
+
+
+ + setNplayers(parseInt(ev.target.value))} + type="number" + value={nplayers} + disabled={started} + /> + +
- +
+ + setMthrows(parseInt(ev.target.value))} + type="number" + value={mthrows} + disabled={started} + /> + +
+
+ + setO1(parseInt(ev.target.value))} + type="number" + value={o1} + disabled={started} + /> + +
+
+ + {started + ? players.map((player, idx) => ( + + {Array.from({ length: mthrows }, (_, i) => { + const th = player.throws[i] + return ( +
+ +

+ {(() => { + switch (th?.type) { + case 'real': + return th.pts + case 'bust': + return `не хватило ${th.remaining === 0 ? 'двойного' : `${th.remaining} очка`}` + case 'finish': + return '' + default: + return '0' + } + })()} +

+
+ ) + })} +
+ )) + : null} + {winner !== undefined ? ( + + 🎉 Игрок {winner + 1} выиграл! + + ) : null}
) -} \ No newline at end of file +} + +function Player({ + player, + children, + idx, +}: { + player: Player + children: ReactNode + idx: number +}) { + const cidx = useGame(game => game.currentPlayerIdx) + return ( + + + Игрок #{idx + 1} + Всего очков: {player.pts} + + {children} + + ) +} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index f000e3e..1238b27 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,26 +1,26 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' -import { cn } from "@/lib/utils" +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", + '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", + 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + '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", + 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + outline: 'text-foreground', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } + }, ) export interface BadgeProps diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 0ba4277..c0ea0e3 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,36 +1,36 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +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" +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", + '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", + default: 'bg-primary text-primary-foreground hover:bg-primary/90', destructive: - "bg-destructive text-destructive-foreground hover:bg-destructive/90", + 'bg-destructive text-destructive-foreground hover:bg-destructive/90', outline: - "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + '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", + '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", + 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", + variant: 'default', + size: 'default', }, - } + }, ) export interface ButtonProps @@ -41,7 +41,7 @@ export interface ButtonProps const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : 'button' return ( ( {...props} /> ) - } + }, ) -Button.displayName = "Button" +Button.displayName = 'Button' export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index afa13ec..c5d18d4 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const Card = React.forwardRef< HTMLDivElement, @@ -9,13 +9,13 @@ const Card = React.forwardRef<
)) -Card.displayName = "Card" +Card.displayName = 'Card' const CardHeader = React.forwardRef< HTMLDivElement, @@ -23,11 +23,11 @@ const CardHeader = React.forwardRef< >(({ className, ...props }, ref) => (
)) -CardHeader.displayName = "CardHeader" +CardHeader.displayName = 'CardHeader' const CardTitle = React.forwardRef< HTMLParagraphElement, @@ -36,13 +36,13 @@ const CardTitle = React.forwardRef<

)) -CardTitle.displayName = "CardTitle" +CardTitle.displayName = 'CardTitle' const CardDescription = React.forwardRef< HTMLParagraphElement, @@ -50,19 +50,19 @@ const CardDescription = React.forwardRef< >(({ className, ...props }, ref) => (

)) -CardDescription.displayName = "CardDescription" +CardDescription.displayName = 'CardDescription' const CardContent = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -

+
)) -CardContent.displayName = "CardContent" +CardContent.displayName = 'CardContent' const CardFooter = React.forwardRef< HTMLDivElement, @@ -70,10 +70,10 @@ const CardFooter = React.forwardRef< >(({ className, ...props }, ref) => (
)) -CardFooter.displayName = "CardFooter" +CardFooter.displayName = 'CardFooter' export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 677d05f..fc5146e 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from 'react' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' export interface InputProps extends React.InputHTMLAttributes {} @@ -11,15 +11,15 @@ const Input = React.forwardRef( ) - } + }, ) -Input.displayName = "Input" +Input.displayName = 'Input' export { Input } diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx index 683faa7..586e08e 100644 --- a/src/components/ui/label.tsx +++ b/src/components/ui/label.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { cva, type VariantProps } from "class-variance-authority" +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" +import { cn } from '@/lib/utils' const labelVariants = cva( - "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" + 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', ) const Label = React.forwardRef< diff --git a/src/index.css b/src/index.css index b0e6fff..8abdb15 100644 --- a/src/index.css +++ b/src/index.css @@ -1,76 +1,76 @@ @tailwind base; - @tailwind components; - @tailwind utilities; +@tailwind components; +@tailwind utilities; - @layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; - --card: 0 0% 100%; - --card-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%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; + --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%; + --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%; + --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%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; + --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%; + --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%; - } + --radius: 0.5rem; } - @layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } - } \ No newline at end of file + .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; + } +} diff --git a/src/lib/math.ts b/src/lib/math.ts new file mode 100644 index 0000000..824afa9 --- /dev/null +++ b/src/lib/math.ts @@ -0,0 +1,77 @@ +import React from 'react' + +/* 9 27 45 63 81 99 117 135 153 171 189 207 225 243 261 279 297 315 333 351 */ +const ptSlice = [ + [1, 153, 171], + [2, 27, 45], + [3, 351, 360], + [3, 0, 9], + [4, 117, 135], + [5, 189, 207], + [6, 81, 99], + [7, 315, 333], + [8, 279, 297], + [9, 225, 243], + [10, 63, 81], + [11, 261, 279], + [12, 207, 225], + [13, 99, 117], + [14, 243, 261], + [15, 45, 63], + [16, 297, 315], + [17, 9, 27], + [18, 135, 153], + [19, 333, 351], + [20, 171, 189], +] + +/* Проверка дистанции */ +/* 3% 7.5% 44% 47% 71,5 75,5 */ +const prRanges = [ + [0, 50, 0.0, 3.0], + [0, 25, 3.0, 7.5], + [1, 0, 7.5, 38.0], + [3, 0, 38.0, 47.3], + [1, 0, 47.3, 65.2], + [2, 0, 65.2, 75.5], + [0, 0, 75.5, 150.0], +] + +export function getPoints(e: React.MouseEvent): { + points: number + double: boolean +} { + let points = 0 + let double = false + let lims = e.currentTarget.getBoundingClientRect() + + let cxval = e.clientX - lims.left - lims.width / 2 + let cyval = e.clientY - lims.top - lims.height / 2 + + let angleval = (Math.atan2(cxval, cyval) * 180) / Math.PI + if (angleval < 0) angleval += 360 + + let distval = Math.sqrt(cxval * cxval + cyval * cyval) + distval = (distval / (lims.width / 2)) * 100 /* in prercent */ + + for (let index = 0; index < ptSlice.length; index++) { + if (angleval > ptSlice[index][1] && angleval <= ptSlice[index][2]) { + points = ptSlice[index][0] + break + } + } + + for (let index = 0; index < prRanges.length; index++) { + if (distval > prRanges[index][2] && distval <= prRanges[index][3]) { + points = points * prRanges[index][0] + prRanges[index][1] + if (prRanges[index][0] == 2) { + double = true + } else { + double = false + } + break + } + } + + return { points, double } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index d084cca..d32b0fe 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,5 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) diff --git a/src/main.tsx b/src/main.tsx index 3d7150d..111ed00 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,7 +2,7 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import './index.css' - +import('eruda').then((eru) => eru.default.init()) ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/tailwind.config.ts b/tailwind.config.ts index 84287e8..f13cb77 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,80 +1,80 @@ -import type { Config } from "tailwindcss" +import type { Config } from 'tailwindcss' const config = { - darkMode: ["class"], + darkMode: ['class'], content: [ './pages/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', './app/**/*.{ts,tsx}', './src/**/*.{ts,tsx}', - ], - prefix: "", + ], + prefix: '', theme: { container: { center: true, - padding: "2rem", + padding: '2rem', screens: { - "2xl": "1400px", + '2xl': '1400px', }, }, extend: { colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", + 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))", + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', }, secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', }, destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', }, muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', }, accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', }, popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', }, card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', }, }, borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", + 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-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, }, - "accordion-up": { - from: { height: "var(--radix-accordion-content-height)" }, - to: { height: "0" }, + '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", + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', }, }, }, - plugins: [require("tailwindcss-animate")], + plugins: [require('tailwindcss-animate')], } satisfies Config -export default config \ No newline at end of file +export default config diff --git a/tsconfig.json b/tsconfig.json index 22f9bf1..1c7bcfc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,10 @@ { "files": [], "compilerOptions": { - "baseUrl": ".", + "baseUrl": ".", "paths": { - "@/*": [ - "./src/*" - ] - }, + "@/*": ["./src/*"] + } }, "references": [ { diff --git a/vite.config.ts b/vite.config.ts index 017b72d..1c5133f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -9,7 +9,7 @@ export default defineConfig({ resolve: { alias: { // @ts-ignore - "@": path.resolve(__dirname, "./src"), + '@': path.resolve(__dirname, './src'), }, }, })