From a6cceb31ea41a7bd60d5ff91c1d2ceb73ad17fc6 Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Fri, 17 Nov 2023 14:34:38 +0100 Subject: [PATCH 1/6] apply suggestions --- front/components/Rack.tsx | 39 ++++++++++---------- front/components/editor/BasketCourt.tsx | 6 +-- front/components/editor/CourtPlayer.tsx | 8 ++-- front/data/Player.ts | 4 +- front/views/Editor.tsx | 49 +++++++++++++++---------- 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/front/components/Rack.tsx b/front/components/Rack.tsx index ad8f354..415ba2f 100644 --- a/front/components/Rack.tsx +++ b/front/components/Rack.tsx @@ -1,40 +1,39 @@ -import {Dispatch, ReactElement, RefObject, SetStateAction, useRef} from "react"; +import {ReactElement, useRef} from "react"; import Draggable from "react-draggable"; -export interface RackInput { +export interface RackProps { id: string, - objects: [ReactElement[], Dispatch>], - canDetach: (ref: RefObject) => boolean, - onElementDetached: (ref: RefObject, el: ReactElement) => void, + objects: E[], + onChange: (objects: E[]) => void, + canDetach: (ref: HTMLDivElement) => boolean, + onElementDetached: (ref: HTMLDivElement, el: E) => void, + render: (e: E) => ReactElement, } -interface RackItemInput { - item: ReactElement, - onTryDetach: (ref: RefObject, el: ReactElement) => void +interface RackItemProps { + item: E, + onTryDetach: (ref: HTMLDivElement, el: E) => void, + render: (e: E) => ReactElement, } /** * A container of draggable objects * */ -export function Rack({id, objects, canDetach, onElementDetached}: RackInput) { - - const [rackObjects, setRackObjects] = objects - +export function Rack({id, objects, onChange, canDetach, onElementDetached, render}: RackProps) { return (
- {rackObjects.map(element => ( + {objects.map(element => ( { if (!canDetach(ref)) return - setRackObjects(objects => { - const index = objects.findIndex(o => o.key === element.key) - return objects.toSpliced(index, 1); - }) + const index = objects.findIndex(o => o.key === element.key) + onChange(objects.toSpliced(index, 1)) onElementDetached(ref, element) }}/> @@ -43,16 +42,16 @@ export function Rack({id, objects, canDetach, onElementDetached}: RackInput) { ) } -function RackItem({item, onTryDetach}: RackItemInput) { +function RackItem({item, onTryDetach, render}: RackItemProps) { const divRef = useRef(null); return ( onTryDetach(divRef, item)}> + onStop={() => onTryDetach(divRef.current!, item)}>
- {item} + {render(item)}
) diff --git a/front/components/editor/BasketCourt.tsx b/front/components/editor/BasketCourt.tsx index 0819633..607ad38 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -1,6 +1,6 @@ import CourtSvg from '../../assets/basketball_court.svg'; import '../../style/basket_court.css'; -import {MouseEvent, ReactElement, useEffect, useRef, useState} from "react"; +import {ReactElement, useEffect, useRef, useState} from "react"; import CourtPlayer from "./CourtPlayer"; import {Player} from "../../data/Player"; @@ -15,8 +15,8 @@ export function BasketCourt({players, onPlayerRemove}: { players: Player[], onPl onPlayerRemove(player)} /> diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 08323f4..5f05e75 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -1,10 +1,10 @@ -import React, {useRef} from "react"; +import {useRef} from "react"; import "../../style/player.css"; import RemoveIcon from "../../assets/icon/remove.svg"; import Draggable, {DraggableBounds} from "react-draggable"; import {PlayerPiece} from "./PlayerPiece"; -export interface PlayerOptions { +export interface PlayerProps { pos: string, team: string, x: number, @@ -16,7 +16,7 @@ export interface PlayerOptions { /** * A player that is placed on the court, which can be selected, and moved in the associated bounds * */ -export default function CourtPlayer({pos, team, x, y, bounds, onRemove}: PlayerOptions) { +export default function CourtPlayer({pos, team, x, y, bounds, onRemove}: PlayerProps) { const ref = useRef(null); return ( @@ -24,7 +24,7 @@ export default function CourtPlayer({pos, team, x, y, bounds, onRemove}: PlayerO handle={".player-piece"} nodeRef={ref} bounds={bounds} - defaultPosition={{x: x, y: y}} + defaultPosition={{x, y}} >
({}); const positions = ["1", "2", "3", "4", "5"] const [allies, setAllies] = useState( - positions.map(pos => ) + positions.map(key => ({team: "allies", key})) ) const [opponents, setOpponents] = useState( - positions.map(pos => ) + positions.map(key => ({team: "opponents", key})) ) const [players, setPlayers] = useState([]); const courtDivContentRef = useRef(null); - const canDetach = (ref: RefObject) => { - const refBounds = ref.current!.getBoundingClientRect(); + const canDetach = (ref: HTMLDivElement) => { + const refBounds = ref.getBoundingClientRect(); const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); // check if refBounds overlaps courtBounds @@ -39,23 +47,23 @@ export default function Editor({id, name}: { id: number, name: string }) { ); } - const onElementDetach = (ref: RefObject, element: ReactElement) => { - const refBounds = ref.current!.getBoundingClientRect(); + const onElementDetach = (ref: HTMLDivElement, element: RackedPlayer) => { + const refBounds = ref.getBoundingClientRect(); const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); const relativeXPixels = refBounds.x - courtBounds.x; const relativeYPixels = refBounds.y - courtBounds.y; - const xPercent = relativeXPixels / courtBounds.width; - const yPercent = relativeYPixels / courtBounds.height; + const xRatio = relativeXPixels / courtBounds.width; + const yRatio = relativeYPixels / courtBounds.height; setPlayers(players => { return [...players, { id: players.length, - team: element.props.team, - position: element.props.text, - right_percentage: xPercent, - bottom_percentage: yPercent + team: element.team, + position: element.key, + rightRatio: xRatio, + bottomRatio: yRatio }] }) } @@ -87,13 +95,17 @@ export default function Editor({id, name}: { id: number, name: string }) {
+ onElementDetached={onElementDetach} + render={({team, key}) => }/> + onElementDetached={onElementDetach} + render={({team, key}) => }/>
@@ -104,16 +116,15 @@ export default function Editor({id, name}: { id: number, name: string }) { const idx = players.indexOf(player) return players.toSpliced(idx, 1) }) - const piece = switch (player.team) { case "opponents": setOpponents(opponents => ( - [...opponents, piece] + [...opponents, {team: player.team, pos: player.position, key: player.position}] )) break case "allies": setAllies(allies => ( - [...allies, piece] + [...allies, {team: player.team, pos: player.position, key: player.position}] )) } }}/> From eb0f270f0376061c99c94ac650c34456bf66b71f Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Mon, 20 Nov 2023 08:04:04 +0100 Subject: [PATCH 2/6] add ball on screen --- front/assets/icon/ball.svg | 62 +++++++++++++++++++++++++ front/components/editor/BallPiece.tsx | 13 ++++++ front/components/editor/CourtPlayer.tsx | 6 ++- front/data/Ball.ts | 15 ++++++ front/data/Player.ts | 7 ++- front/style/ball.css | 8 ++++ front/style/colors.css | 2 +- front/style/player.css | 4 +- front/views/Editor.tsx | 50 +++++++++++++++++++- 9 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 front/assets/icon/ball.svg create mode 100644 front/components/editor/BallPiece.tsx create mode 100644 front/data/Ball.ts create mode 100644 front/style/ball.css diff --git a/front/assets/icon/ball.svg b/front/assets/icon/ball.svg new file mode 100644 index 0000000..6351088 --- /dev/null +++ b/front/assets/icon/ball.svg @@ -0,0 +1,62 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + + + + + diff --git a/front/components/editor/BallPiece.tsx b/front/components/editor/BallPiece.tsx new file mode 100644 index 0000000..79e8148 --- /dev/null +++ b/front/components/editor/BallPiece.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +import "../../style/ball.css"; + +import Ball from "../../assets/icon/ball.svg"; + +export function BallPiece() { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 5f05e75..21a088e 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -1,6 +1,7 @@ import {useRef} from "react"; import "../../style/player.css"; import RemoveIcon from "../../assets/icon/remove.svg"; +import AssignBallIcon from "../../assets/icon/ball.svg"; import Draggable, {DraggableBounds} from "react-draggable"; import {PlayerPiece} from "./PlayerPiece"; @@ -10,13 +11,14 @@ export interface PlayerProps { x: number, y: number, bounds: DraggableBounds, - onRemove: () => void + onRemove: () => void, + hasBall: boolean } /** * A player that is placed on the court, which can be selected, and moved in the associated bounds * */ -export default function CourtPlayer({pos, team, x, y, bounds, onRemove}: PlayerProps) { +export default function CourtPlayer({pos, team, x, y, bounds, onRemove, hasBall}: PlayerProps) { const ref = useRef(null); return ( diff --git a/front/data/Ball.ts b/front/data/Ball.ts new file mode 100644 index 0000000..c312fdf --- /dev/null +++ b/front/data/Ball.ts @@ -0,0 +1,15 @@ +export interface Ball { + + position: string, + + /** + * Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) + */ + bottom_percentage: number, + + /** + * Percentage of the player's position to the right (0 means left, 1 means right, 0.5 means middle) + */ + right_percentage: number, + +} \ No newline at end of file diff --git a/front/data/Player.ts b/front/data/Player.ts index 9484dd3..983b650 100644 --- a/front/data/Player.ts +++ b/front/data/Player.ts @@ -18,10 +18,15 @@ export interface Player { /** * Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) */ - bottomRatio: number + bottomRatio: number, + /** * Percentage of the player's position to the right (0 means left, 1 means right, 0.5 means middle) */ rightRatio: number, + + + hasBall: boolean, + } \ No newline at end of file diff --git a/front/style/ball.css b/front/style/ball.css new file mode 100644 index 0000000..5669b07 --- /dev/null +++ b/front/style/ball.css @@ -0,0 +1,8 @@ +#ball * { + fill: #c5520d; +} + +#ball { + width: 20px; + height: 20px; +} diff --git a/front/style/colors.css b/front/style/colors.css index f3287cb..54ee221 100644 --- a/front/style/colors.css +++ b/front/style/colors.css @@ -6,7 +6,7 @@ --background-color: #d2cdd3; - --selected-team-primarycolor: #ffffff; + --selected-team-primarycolor: #50b63a; --selected-team-secondarycolor: #000000; --selection-color: #3f7fc4 diff --git a/front/style/player.css b/front/style/player.css index ebd0462..d22a35f 100644 --- a/front/style/player.css +++ b/front/style/player.css @@ -27,9 +27,11 @@ on the court. background-color: var(--selected-team-primarycolor); color: var(--selected-team-secondarycolor); - border-width: 2px; border-radius: 100px; + /* + border-width: 2px; border-style: solid; + */ width: 20px; height: 20px; diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 05dc4a1..487ee51 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -7,6 +7,8 @@ import {BasketCourt} from "../components/editor/BasketCourt"; import {Rack} from "../components/Rack"; import {PlayerPiece} from "../components/editor/PlayerPiece"; import {Player} from "../data/Player"; +import {BallPiece} from "../components/editor/BallPiece"; +import {Ball} from "../data/Ball"; const ERROR_STYLE: CSSProperties = { borderColor: "red" @@ -24,6 +26,7 @@ export default function Editor({id, name}: { id: number, name: string }) { const [style, setStyle] = useState({}); const positions = ["1", "2", "3", "4", "5"] + const positionBall = ["1"] const [allies, setAllies] = useState( positions.map(key => ({team: "allies", key})) ) @@ -31,6 +34,9 @@ export default function Editor({id, name}: { id: number, name: string }) { positions.map(key => ({team: "opponents", key})) ) + const [ballPiece, setBallPiece] = useState(positionBall) + const [ball, setBall] = useState([]); + const [players, setPlayers] = useState([]); const courtDivContentRef = useRef(null); @@ -63,7 +69,42 @@ export default function Editor({id, name}: { id: number, name: string }) { team: element.team, position: element.key, rightRatio: xRatio, - bottomRatio: yRatio + bottomRatio: yRatio, + hasBall:false + }] + }) + } + + const canDetachBall = (ref: HTMLDivElement) => { + const refBounds = ref.getBoundingClientRect(); + const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); + + //check if we give the ball to a player on the court + if (!canDetach(ref)) { + return false; + } + for(const player in players) { + const rightRatio = player + } + return false; + } + + const onElementDetachBall = (ref: RefObject, element: ReactElement) => { + const refBounds = ref.current!.getBoundingClientRect(); + const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); + + const relativeXPixels = refBounds.x - courtBounds.x; + const relativeYPixels = refBounds.y - courtBounds.y; + + const xPercent = relativeXPixels / courtBounds.width; + const yPercent = relativeYPixels / courtBounds.height; + + + setBall(ball => { + return [...ball, { + position: element.props.text, + right_percentage: xPercent, + bottom_percentage: yPercent, }] }) } @@ -100,6 +141,12 @@ export default function Editor({id, name}: { id: number, name: string }) { canDetach={canDetach} onElementDetached={onElementDetach} render={({team, key}) => }/> + }/> { setPlayers(players => { const idx = players.indexOf(player) From 7ab18786ccbc35ca158f5bce869ffaf340319258 Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Mon, 4 Dec 2023 17:38:30 +0100 Subject: [PATCH 3/6] can assign ball to player in the code (need render and destroy ball now) --- front/components/editor/BallPiece.tsx | 2 +- front/components/editor/CourtPlayer.tsx | 6 +- front/tactic/Player.ts | 2 + front/views/Editor.tsx | 126 +++++++++++------------- 4 files changed, 63 insertions(+), 73 deletions(-) diff --git a/front/components/editor/BallPiece.tsx b/front/components/editor/BallPiece.tsx index 79e8148..a99abf0 100644 --- a/front/components/editor/BallPiece.tsx +++ b/front/components/editor/BallPiece.tsx @@ -2,7 +2,7 @@ import React from "react"; import "../../style/ball.css"; -import Ball from "../../assets/icon/ball.svg"; +import Ball from "../../assets/icon/ball.svg?react"; export function BallPiece() { return ( diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 6431a50..9ca517a 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -21,7 +21,6 @@ export default function CourtPlayer({ player, onChange, onRemove, - assignBall, parentRef, }: PlayerProps) { const pieceRef = useRef(null) @@ -42,11 +41,12 @@ export default function CourtPlayer({ const { x, y } = calculateRatio(pieceBounds, parentBounds) onChange({ + id : player.id, rightRatio: x, bottomRatio: y, team: player.team, role: player.role, - hasBall: false + hasBall: player.hasBall }) }}>
-
{ diff --git a/front/tactic/Player.ts b/front/tactic/Player.ts index 13d79fe..32ed02d 100644 --- a/front/tactic/Player.ts +++ b/front/tactic/Player.ts @@ -1,6 +1,8 @@ import { Team } from "./Team" export interface Player { + + id : string /** * the player's team * */ diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index c3f1a29..40cb9ea 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -8,22 +8,23 @@ import { } from "react" import "../style/editor.css" import TitleInput from "../components/TitleInput" -import { BasketCourt } from "../components/editor/BasketCourt" - - -import { BallPiece } from "../components/editor/BallPiece"; -import { Ball } from "../tactic/Ball"; -import { Rack } from "../components/Rack" -import { PlayerPiece } from "../components/editor/PlayerPiece" -import { Player } from "../tactic/Player" -import { Tactic, TacticContent } from "../tactic/Tactic" -import { fetchAPI } from "../Fetcher" -import { Team } from "../tactic/Team" -import { calculateRatio } from "../Utils" +import {BasketCourt} from "../components/editor/BasketCourt" + + +import {BallPiece} from "../components/editor/BallPiece"; +import {Ball} from "../tactic/Ball"; +import {Rack} from "../components/Rack" +import {PlayerPiece} from "../components/editor/PlayerPiece" +import {Player} from "../tactic/Player" +import {Tactic, TacticContent} from "../tactic/Tactic" +import {fetchAPI} from "../Fetcher" +import {Team} from "../tactic/Team" +import {calculateRatio} from "../Utils" import SavingState, { SaveState, SaveStates, } from "../components/editor/SavingState" +import Draggable from "react-draggable"; const ERROR_STYLE: CSSProperties = { borderColor: "red", @@ -47,10 +48,10 @@ interface RackedPlayer { } export default function Editor({ - id, - name, - content, -}: { + id, + name, + content, + }: { id: number name: string content: string @@ -66,7 +67,7 @@ export default function Editor({ return ( { if (isInGuestMode) { localStorage.setItem( @@ -75,7 +76,7 @@ export default function Editor({ ) return SaveStates.Guest } - return fetchAPI(`tactic/${id}/save`, { content }).then((r) => + return fetchAPI(`tactic/${id}/save`, {content}).then((r) => r.ok ? SaveStates.Ok : SaveStates.Err, ) }} @@ -84,7 +85,7 @@ export default function Editor({ localStorage.setItem(GUEST_MODE_TITLE_STORAGE_KEY, name) return true //simulate that the name has been changed } - return fetchAPI(`tactic/${id}/edit/name`, { name }).then( + return fetchAPI(`tactic/${id}/edit/name`, {name}).then( (r) => r.ok, ) }} @@ -94,10 +95,10 @@ export default function Editor({ function EditorView({ - tactic: { id, name, content: initialContent }, - onContentChange, - onNameChange, -}: EditorViewProps) { + tactic: {id, name, content: initialContent}, + onContentChange, + onNameChange, + }: EditorViewProps) { const isInGuestMode = id == -1 const [style, setStyle] = useState({}) @@ -113,8 +114,7 @@ function EditorView({ getRackPlayers(Team.Opponents, content.players), ) - - const [ball, setBall] = useState([]); + const ballPiece = useRef(null) const courtDivContentRef = useRef(null) @@ -136,13 +136,14 @@ function EditorView({ const refBounds = ref.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect() - const { x, y } = calculateRatio(refBounds, courtBounds) + const {x, y} = calculateRatio(refBounds, courtBounds) setContent((content) => { return { players: [ ...content.players, { + id: "player-" + content.players.length, team: element.team, role: element.key, rightRatio: x, @@ -154,37 +155,22 @@ function EditorView({ }) } - const canDetachBall = (ref: HTMLDivElement) => { - const refBounds = ref.getBoundingClientRect(); - const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); - - //check if we give the ball to a player on the court - if (!canDetach(ref)) { - return false; + const onElementDetachBall = () => { + const ballBounds = ballPiece.current!.getBoundingClientRect() + + for (const player of content.players) { + const playerBounds = document.getElementById(player.id)!.getBoundingClientRect() + const doesNotOverlap = ( + ballBounds.top > playerBounds.bottom || + ballBounds.right < playerBounds.left || + ballBounds.bottom < playerBounds.top || + ballBounds.left > playerBounds.right + ) + if (doesNotOverlap) { + continue + } + player.hasBall = true } - /*for(const player in players) { - const rightRatio = player - }*/ - return false; - } - - const onElementDetachBall = (ref: HTMLDivElement) => { - const refBounds = ref.getBoundingClientRect() - const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); - - const relativeXPixels = refBounds.x - courtBounds.x; - const relativeYPixels = refBounds.y - courtBounds.y; - - const xPercent = relativeXPixels / courtBounds.width; - const yPercent = relativeYPixels / courtBounds.height; - - - setBall(ball => { - return [...ball, { - right_percentage: xPercent, - bottom_percentage: yPercent, - }] - }) } @@ -193,7 +179,7 @@ function EditorView({
LEFT - +
( - + render={({team, key}) => ( + )} /> - }/> + + +
+ +
+
+ ( - + render={({team, key}) => ( + )} />
@@ -291,7 +279,7 @@ function getRackPlayers(team: Team, players: Player[]): RackedPlayer[] { players.findIndex((p) => p.team == team && p.role == role) == -1, ) - .map((key) => ({ team, key })) + .map((key) => ({team, key})) } function useContentState( From 7177c07ca049a391b8854ebce67bfd868afa97de Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Tue, 5 Dec 2023 18:02:26 +0100 Subject: [PATCH 4/6] can assign ball to a player, player with ball is surrounded --- front/components/editor/BallPiece.tsx | 23 ++++++++--- front/components/editor/BasketCourt.tsx | 3 ++ front/components/editor/CourtPlayer.tsx | 10 ++++- front/components/editor/PlayerPiece.tsx | 15 ++++++- front/style/ball.css | 6 ++- front/style/player.css | 9 ++-- front/views/Editor.tsx | 55 ++++++++++++++----------- 7 files changed, 81 insertions(+), 40 deletions(-) diff --git a/front/components/editor/BallPiece.tsx b/front/components/editor/BallPiece.tsx index a99abf0..054f83c 100644 --- a/front/components/editor/BallPiece.tsx +++ b/front/components/editor/BallPiece.tsx @@ -1,13 +1,26 @@ -import React from "react"; +import React, {RefObject} from "react"; import "../../style/ball.css"; import Ball from "../../assets/icon/ball.svg?react"; +import Draggable from "react-draggable"; -export function BallPiece() { +export interface BallPieceProps { + onDrop: () => void + pieceRef: RefObject +} + + +export function BallPiece({onDrop, pieceRef}: BallPieceProps) { return ( -
- -
+ +
+ +
+
) } \ No newline at end of file diff --git a/front/components/editor/BasketCourt.tsx b/front/components/editor/BasketCourt.tsx index b583f61..c15f02c 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -8,12 +8,14 @@ import { Player } from "../../tactic/Player" export interface BasketCourtProps { players: Player[] onPlayerRemove: (p: Player) => void + onBallDrop: (ref : HTMLDivElement) => void onPlayerChange: (p: Player) => void } export function BasketCourt({ players, onPlayerRemove, + onBallDrop, onPlayerChange, }: BasketCourtProps) { const divRef = useRef(null) @@ -28,6 +30,7 @@ export function BasketCourt({ player={player} onChange={onPlayerChange} onRemove={() => onPlayerRemove(player)} + onBallDrop={onBallDrop} parentRef={divRef} /> ) diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 9ca517a..1d4961a 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -1,7 +1,7 @@ import { RefObject, useRef, useState } from "react" import "../../style/player.css" import RemoveIcon from "../../assets/icon/remove.svg?react" -import BallIcon from "../../assets/icon/ball.svg?react" +import {BallPiece} from "./BallPiece"; import Draggable from "react-draggable" import { PlayerPiece } from "./PlayerPiece" import { Player } from "../../tactic/Player" @@ -11,6 +11,7 @@ export interface PlayerProps { player: Player onChange: (p: Player) => void onRemove: () => void + onBallDrop: (ref: HTMLDivElement) => void parentRef: RefObject } @@ -21,12 +22,15 @@ export default function CourtPlayer({ player, onChange, onRemove, + onBallDrop, parentRef, }: PlayerProps) { const pieceRef = useRef(null) + const ballPiece = useRef(null) const x = player.rightRatio const y = player.bottomRatio + const hasBall = player.hasBall return ( + {hasBall && onBallDrop(ballPiece.current!)} pieceRef={ballPiece}/>}
- +
diff --git a/front/components/editor/PlayerPiece.tsx b/front/components/editor/PlayerPiece.tsx index 69b38c2..5756afb 100644 --- a/front/components/editor/PlayerPiece.tsx +++ b/front/components/editor/PlayerPiece.tsx @@ -2,9 +2,20 @@ import React from "react" import "../../style/player.css" import { Team } from "../../tactic/Team" -export function PlayerPiece({ team, text }: { team: Team; text: string }) { +export interface PlayerPieceProps { + team: Team + text: string + hasBall : boolean +} + +export function PlayerPiece({ team, text, hasBall}: PlayerPieceProps) { + let className = `player-piece ${team}` + if (hasBall) { + className += ` player-piece-has-ball` + } + return ( -
+

{text}

) diff --git a/front/style/ball.css b/front/style/ball.css index 5669b07..d79ac89 100644 --- a/front/style/ball.css +++ b/front/style/ball.css @@ -1,8 +1,10 @@ -#ball * { +.ball * { fill: #c5520d; } -#ball { +.ball { + pointer-events: all; width: 20px; height: 20px; + cursor: pointer; } diff --git a/front/style/player.css b/front/style/player.css index 166b449..be468e6 100644 --- a/front/style/player.css +++ b/front/style/player.css @@ -24,10 +24,6 @@ on the court. color: var(--selected-team-secondarycolor); border-radius: 100px; - /* - border-width: 2px; - border-style: solid; - */ width: 20px; height: 20px; @@ -40,6 +36,11 @@ on the court. user-select: none; } +.player-piece-has-ball { + border-width: 2px; + border-style: solid; +} + .player-selection-tab { display: flex; diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 40cb9ea..0923a67 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -114,6 +114,8 @@ function EditorView({ getRackPlayers(Team.Opponents, content.players), ) + const [showBall, setShowBall] = useState(true) + const ballPiece = useRef(null) const courtDivContentRef = useRef(null) @@ -155,22 +157,29 @@ function EditorView({ }) } - const onElementDetachBall = () => { - const ballBounds = ballPiece.current!.getBoundingClientRect() - - for (const player of content.players) { - const playerBounds = document.getElementById(player.id)!.getBoundingClientRect() - const doesNotOverlap = ( - ballBounds.top > playerBounds.bottom || - ballBounds.right < playerBounds.left || - ballBounds.bottom < playerBounds.top || - ballBounds.left > playerBounds.right - ) - if (doesNotOverlap) { - continue - } - player.hasBall = true - } + const onBallDrop = (ref : HTMLDivElement) => { + const ballBounds = ref.getBoundingClientRect() + let showBall = true + + setContent(content => { + const players = content.players.map(player => { + const playerBounds = document.getElementById(player.id)!.getBoundingClientRect() + const doesOverlap = !( + ballBounds.top > playerBounds.bottom || + ballBounds.right < playerBounds.left || + ballBounds.bottom < playerBounds.top || + ballBounds.left > playerBounds.right + ) + if(doesOverlap) { + showBall = false + + } + + return {...player, hasBall: doesOverlap} + }) + setShowBall(showBall) + return {players: players} + }) } @@ -203,17 +212,11 @@ function EditorView({ canDetach={canDetach} onElementDetached={onPieceDetach} render={({team, key}) => ( - + )} /> - -
- -
-
+ {showBall && onBallDrop(ballPiece.current!)} pieceRef={ballPiece}/>} ( - + )} />
@@ -230,6 +233,7 @@ function EditorView({
{ setContent((content) => ({ players: toSplicedPlayers( @@ -264,6 +268,7 @@ function EditorView({ }, ]) }} + />
From b653ff4ea299f465d2ffe15cc9d58b43d4860453 Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Tue, 5 Dec 2023 18:25:53 +0100 Subject: [PATCH 5/6] fix collision problem where more than 1 player can have the ball --- front/style/player.css | 6 ++++-- front/views/Editor.tsx | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/front/style/player.css b/front/style/player.css index be468e6..facdaa2 100644 --- a/front/style/player.css +++ b/front/style/player.css @@ -44,8 +44,8 @@ on the court. .player-selection-tab { display: flex; - position: absolute; - margin-bottom: 10%; + position: relative; + margin-bottom: -20%; justify-content: center; visibility: hidden; @@ -56,6 +56,8 @@ on the court. .player-selection-tab-remove { pointer-events: all; height: 25%; + width: 25%; + justify-content: center; } .player-selection-tab-remove * { diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 0923a67..17276c0 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -159,10 +159,13 @@ function EditorView({ const onBallDrop = (ref : HTMLDivElement) => { const ballBounds = ref.getBoundingClientRect() - let showBall = true + let ballAssigned = false setContent(content => { const players = content.players.map(player => { + if(ballAssigned) { + return {...player, hasBall: false} + } const playerBounds = document.getElementById(player.id)!.getBoundingClientRect() const doesOverlap = !( ballBounds.top > playerBounds.bottom || @@ -171,13 +174,11 @@ function EditorView({ ballBounds.left > playerBounds.right ) if(doesOverlap) { - showBall = false - + ballAssigned = true } - return {...player, hasBall: doesOverlap} }) - setShowBall(showBall) + setShowBall(!ballAssigned) return {players: players} }) } @@ -259,6 +260,9 @@ function EditorView({ case Team.Allies: setter = setAllies } + if(player.hasBall) { + setShowBall(true) + } setter((players) => [ ...players, { From 31fad085ebeb32d1e7c2de00052fe55ddfd35410 Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Tue, 5 Dec 2023 18:47:42 +0100 Subject: [PATCH 6/6] fix css --- front/style/ball.css | 2 +- front/style/colors.css | 2 ++ front/style/player.css | 9 +++++---- front/views/Editor.tsx | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/front/style/ball.css b/front/style/ball.css index d79ac89..5bf15d1 100644 --- a/front/style/ball.css +++ b/front/style/ball.css @@ -2,7 +2,7 @@ fill: #c5520d; } -.ball { +.ball-div, .ball { pointer-events: all; width: 20px; height: 20px; diff --git a/front/style/colors.css b/front/style/colors.css index 1ab3f01..53a9f65 100644 --- a/front/style/colors.css +++ b/front/style/colors.css @@ -8,4 +8,6 @@ --selected-team-secondarycolor: #000000; --selection-color: #3f7fc4; + + --player-piece-ball-border-color: #000000; } diff --git a/front/style/player.css b/front/style/player.css index facdaa2..d79cf46 100644 --- a/front/style/player.css +++ b/front/style/player.css @@ -39,24 +39,25 @@ on the court. .player-piece-has-ball { border-width: 2px; border-style: solid; + border-color: var(--player-piece-ball-border-color); } .player-selection-tab { display: flex; - position: relative; + position: absolute; margin-bottom: -20%; justify-content: center; visibility: hidden; - width: 100%; + width: fit-content; transform: translateY(-20px); } .player-selection-tab-remove { pointer-events: all; - height: 25%; - width: 25%; + width: 25px; + height: 17px; justify-content: center; } diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 17276c0..19b8a15 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -114,7 +114,7 @@ function EditorView({ getRackPlayers(Team.Opponents, content.players), ) - const [showBall, setShowBall] = useState(true) + const [showBall, setShowBall] = useState(content.players.find(p => p.hasBall) == undefined) const ballPiece = useRef(null)