diff --git a/front/components/editor/BallPiece.tsx b/front/components/editor/BallPiece.tsx index 2c3b3b0..6390530 100644 --- a/front/components/editor/BallPiece.tsx +++ b/front/components/editor/BallPiece.tsx @@ -1,27 +1,17 @@ -import React, {RefObject, useRef} from "react" - import "../../style/ball.css" -import Ball from "../../assets/icon/ball.svg?react" -import Draggable from "react-draggable" +import BallSvg from "../../assets/icon/ball.svg?react" +import {Ball} from "../../tactic/CourtObjects"; export interface CourtBallProps { - onDrop: (pos: DOMRect) => void + onMoved: (rect: DOMRect) => void + ball: Ball } -export function CourtBall({ onDrop }: CourtBallProps) { - const ref = useRef() - return ( - onDrop(ref.current!.getBoundingClientRect())} nodeRef={ref}> - - - ) -} -export function BallPiece({pieceRef}: {pieceRef: RefObject}) { +export function BallPiece() { return ( -
- -
+ ) } + diff --git a/front/components/editor/BasketCourt.tsx b/front/components/editor/BasketCourt.tsx index bca8a72..bcf8284 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -1,36 +1,41 @@ import "../../style/basket_court.css" -import { RefObject, useRef } from "react" +import {RefObject} from "react" import CourtPlayer from "./CourtPlayer" -import { Player } from "../../tactic/Player" -import {BallPiece, CourtBall} from "./BallPiece"; -import {Ball} from "../../tactic/Ball"; + +import {Player} from "../../tactic/Player" +import {CourtObject} from "../../tactic/CourtObjects"; +import {CourtBall} from "./CourtBall"; export interface BasketCourtProps { players: Player[] - ball: Ball + objects: CourtObject[] + onPlayerRemove: (p: Player) => void - onBallDrop: (b: DOMRect) => void onPlayerChange: (p: Player) => void + + onBallMoved: (ball: DOMRect) => void, + courtImage: string courtRef: RefObject } export function BasketCourt({ - players, - ball, - onPlayerRemove, - onBallDrop, - onPlayerChange, - courtImage, - courtRef, -}: BasketCourtProps) { + + players, + objects, + onPlayerRemove, + onBallMoved, + onPlayerChange, + courtImage, + courtRef, + }: BasketCourtProps) { return (
- {"court"} + style={{position: "relative"}}> + {"court"} {players.map((player) => { return ( onPlayerRemove(player)} - onBallDrop={onBallDrop} + onBallDrop={onBallMoved} parentRef={courtRef} /> ) })} - + + {objects.map(object => { + if (object.type == "ball") { + return + } + throw new Error("unknown court object", object.type) + })}
) } + + diff --git a/front/components/editor/CourtBall.tsx b/front/components/editor/CourtBall.tsx new file mode 100644 index 0000000..5702002 --- /dev/null +++ b/front/components/editor/CourtBall.tsx @@ -0,0 +1,28 @@ +import React, {useRef} from "react"; +import Draggable from "react-draggable"; +import {BallPiece, CourtBallProps} from "./BallPiece"; + +export function CourtBall({onMoved, ball}: CourtBallProps) { + const pieceRef = useRef(null) + + const x = ball.rightRatio + const y = ball.bottomRatio + + return ( + onMoved(pieceRef.current!.getBoundingClientRect())} + nodeRef={pieceRef} + > +
+ +
+
+ ) +} \ No newline at end of file diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 5ebeedf..8f4f659 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -1,16 +1,17 @@ -import { RefObject, useRef, useState } from "react" +import {RefObject, useRef} from "react" import "../../style/player.css" -import { BallPiece } from "./BallPiece" +import RemoveIcon from "../../assets/icon/remove.svg?react" +import {BallPiece} from "./BallPiece" import Draggable from "react-draggable" -import { PlayerPiece } from "./PlayerPiece" -import { Player } from "../../tactic/Player" -import { calculateRatio } from "../../Utils" +import {PlayerPiece} from "./PlayerPiece" +import {Player} from "../../tactic/Player" +import {calculateRatio} from "../../Utils" export interface PlayerProps { player: Player onChange: (p: Player) => void onRemove: () => void - onBallDrop: (b: DOMRect) => void + onBallDrop: (bounds: DOMRect) => void parentRef: RefObject } @@ -25,6 +26,7 @@ export default function CourtPlayer({ parentRef, }: PlayerProps) { const pieceRef = useRef(null) + const ballPiece = useRef(null) const x = player.rightRatio const y = player.bottomRatio @@ -67,12 +69,15 @@ export default function CourtPlayer({ if (e.key == "Delete") onRemove() }}>
- {hasBall && ( - onBallDrop(ball)} - pieceRef={ball} - /> - )} + + {hasBall && ( onBallDrop(ballPiece.current!.getBoundingClientRect())}> +
+ +
+
)}
p.hasBall) == undefined, - ) - - const ballPiece = useRef(null) + const [objects, setObjects] = useState([{key: "ball"}]) const courtDivContentRef = useRef(null) + const canDetach = (bounds: DOMRect) => { const courtBounds = courtDivContentRef.current!.getBoundingClientRect() @@ -159,6 +151,7 @@ function EditorView({ setContent((content) => { return { + ...content, players: [ ...content.players, { @@ -170,14 +163,43 @@ function EditorView({ hasBall: false, }, ], - ball: content.ball } }) } + const onObjectDetach = (ref: HTMLDivElement, rackedObject: RackedCourtObject) => { + const refBounds = ref.getBoundingClientRect() + const courtBounds = courtDivContentRef.current!.getBoundingClientRect() + + const {x, y} = calculateRatio(refBounds, courtBounds) + + let courtObject: CourtObject + switch (rackedObject.key) { + case "ball": + courtObject = { + type: "ball", + rightRatio: x, + bottomRatio: y + } + break + default: + throw new Error("unknown court object ", rackedObject.key) + } + + setContent((content) => + ({ + ...content, + objects: [ + ...content.objects, + courtObject, + ] + }) + ) + } + const onBallDrop = (ballBounds: DOMRect) => { let ballAssigned = false @@ -200,7 +222,14 @@ function EditorView({ } return { ...player, hasBall: doesOverlap } }) - return {players: players, ball: content.ball} + + let objects = content.objects + if (ballAssigned) { + const ballPieceIdx = content.objects.findIndex(obj => obj.type === "ball") + objects = objects.toSpliced(ballPieceIdx, 1) + } + + return {...content, objects, players} }) } @@ -231,7 +260,7 @@ function EditorView({ id="allies-rack" objects={allies} onChange={setAllies} - canDetach={canDetach} + canDetach={div => canDetach(div.getBoundingClientRect())} onElementDetached={onPieceDetach} render={({ team, key }) => ( - {rackBall && { - if (canDetach(pos)) { - onBallDetach(pos) - } - }}/>} + canDetach(div.getBoundingClientRect())} + onElementDetached={onObjectDetach} + render={renderCourtObject}/> canDetach(div.getBoundingClientRect())} onElementDetached={onPieceDetach} render={({ team, key }) => ( { setContent((content) => ({ + ...content, players: toSplicedPlayers( content.players, player, true, ), - ball: content.ball })) }} onPlayerRemove={(player) => { setContent((content) => ({ + ...content, players: toSplicedPlayers( content.players, player, false, ), - ball: content.ball })) let setter switch (player.team) { @@ -303,9 +333,8 @@ function EditorView({ setter = setAllies } if (player.hasBall) { - setShowBall(true) + /// add an instance of RackedBall back to the rack (objects) } - setter((players) => [ ...players, { @@ -323,6 +352,13 @@ function EditorView({ ) } +function renderCourtObject(courtObject: RackedCourtObject) { + if (courtObject.key == "ball") { + return + } + throw new Error("unknown racked court object ", courtObject.key) +} + function getRackPlayers(team: Team, players: Player[]): RackedPlayer[] { return ["1", "2", "3", "4", "5"] .filter( diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index efb20c6..0cbe32b 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -19,7 +19,7 @@ CREATE TABLE Tactic name varchar NOT NULL, creation_date timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, owner integer NOT NULL, - content varchar DEFAULT '{"players": []}' NOT NULL, + content varchar DEFAULT '{"players": [], "objects": []}' NOT NULL, court_type varchar CHECK ( court_type IN ('HALF', 'PLAIN')) NOT NULL, FOREIGN KEY (owner) REFERENCES Account ); diff --git a/src/App/Controller/EditorController.php b/src/App/Controller/EditorController.php index 3bbbe61..8415cdb 100644 --- a/src/App/Controller/EditorController.php +++ b/src/App/Controller/EditorController.php @@ -42,7 +42,7 @@ class EditorController { return ViewHttpResponse::react("views/Editor.tsx", [ "id" => -1, //-1 id means that the editor will not support saves "name" => TacticModel::TACTIC_DEFAULT_NAME, - "content" => '{"players": []}', + "content" => '{"players": [], "objects": []}', "courtType" => $courtType->name(), ]); }