From c53a1b024c6b026e2aec5c1ccfe3e6aa66ad477f Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Fri, 17 Nov 2023 14:34:38 +0100 Subject: [PATCH] apply suggestions --- front/components/Rack.tsx | 39 ++++++++-------- front/components/editor/BasketCourt.tsx | 38 ++++++--------- front/components/editor/CourtPlayer.tsx | 30 ++++++------ front/components/editor/PlayerPiece.tsx | 3 +- front/data/Player.ts | 10 ++-- front/data/Team.tsx | 4 ++ front/style/basket_court.css | 1 + front/style/editor.css | 5 ++ front/style/player.css | 10 ++-- front/views/Editor.tsx | 62 ++++++++++++++++--------- tsconfig.json | 2 +- vite.config.ts | 2 +- 12 files changed, 116 insertions(+), 90 deletions(-) create mode 100644 front/data/Team.tsx diff --git a/front/components/Rack.tsx b/front/components/Rack.tsx index ad8f354..09681e7 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..b0b0eda 100644 --- a/front/components/editor/BasketCourt.tsx +++ b/front/components/editor/BasketCourt.tsx @@ -1,35 +1,27 @@ -import CourtSvg from '../../assets/basketball_court.svg'; +import CourtSvg from '../../assets/basketball_court.svg?react'; import '../../style/basket_court.css'; -import {MouseEvent, ReactElement, useEffect, useRef, useState} from "react"; +import {useRef} from "react"; import CourtPlayer from "./CourtPlayer"; import {Player} from "../../data/Player"; -export function BasketCourt({players, onPlayerRemove}: { players: Player[], onPlayerRemove: (Player) => void }) { - const [courtPlayers, setCourtPlayers] = useState([]) - const divRef = useRef(null); +export interface BasketCourtProps { + players: Player[], + onPlayerRemove: (p: Player) => void, +} - useEffect(() => { - const bounds = divRef.current!.getBoundingClientRect(); - setCourtPlayers(players.map(player => { - return ( - onPlayerRemove(player)} - /> - ) - })) - }, [players, divRef]); +export function BasketCourt({players, onPlayerRemove}: BasketCourtProps) { + const divRef = useRef(null); return ( -
+
- {courtPlayers} + {players.map(player => { + return onPlayerRemove(player)} + /> + })}
) } - diff --git a/front/components/editor/CourtPlayer.tsx b/front/components/editor/CourtPlayer.tsx index 08323f4..b6b64de 100644 --- a/front/components/editor/CourtPlayer.tsx +++ b/front/components/editor/CourtPlayer.tsx @@ -1,35 +1,37 @@ -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 RemoveIcon from "../../assets/icon/remove.svg?react"; +import Draggable from "react-draggable"; import {PlayerPiece} from "./PlayerPiece"; +import {Player} from "../../data/Player"; -export interface PlayerOptions { - pos: string, - team: string, - x: number, - y: number, - bounds: DraggableBounds, +export interface PlayerProps { + player: Player, onRemove: () => void } /** * 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({player, onRemove}: PlayerProps) { const ref = useRef(null); + + const x = player.rightRatio; + const y = player.bottomRatio; + return (
onRemove()}/> + onClick={onRemove}/>
- +
diff --git a/front/components/editor/PlayerPiece.tsx b/front/components/editor/PlayerPiece.tsx index b5cc41f..83e7dfc 100644 --- a/front/components/editor/PlayerPiece.tsx +++ b/front/components/editor/PlayerPiece.tsx @@ -1,8 +1,9 @@ import React from "react"; import '../../style/player.css' +import {Team} from "../../data/Team"; -export function PlayerPiece({team, text}: { team: string, text: string }) { +export function PlayerPiece({team, text}: { team: Team, text: string }) { return (

{text}

diff --git a/front/data/Player.ts b/front/data/Player.ts index a27e643..af88c1c 100644 --- a/front/data/Player.ts +++ b/front/data/Player.ts @@ -1,3 +1,5 @@ +import {Team} from "./Team"; + export interface Player { /** * unique identifier of the player. @@ -8,20 +10,20 @@ export interface Player { /** * the player's team * */ - team: "allies" | "opponents", + team: Team, /** * player's position * */ - position: string, + role: string, /** * Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) */ - bottom_percentage: number + bottomRatio: number /** * Percentage of the player's position to the right (0 means left, 1 means right, 0.5 means middle) */ - right_percentage: number, + rightRatio: number, } \ No newline at end of file diff --git a/front/data/Team.tsx b/front/data/Team.tsx new file mode 100644 index 0000000..ea4c384 --- /dev/null +++ b/front/data/Team.tsx @@ -0,0 +1,4 @@ +export enum Team { + Allies = "allies", + Opponents = "opponents" +} \ No newline at end of file diff --git a/front/style/basket_court.css b/front/style/basket_court.css index 920512b..a5bc688 100644 --- a/front/style/basket_court.css +++ b/front/style/basket_court.css @@ -3,6 +3,7 @@ #court-container { display: flex; + background-color: var(--main-color); } diff --git a/front/style/editor.css b/front/style/editor.css index e2d38c9..3aad26c 100644 --- a/front/style/editor.css +++ b/front/style/editor.css @@ -52,3 +52,8 @@ #court-div-bounds { width: 60%; } + + +.react-draggable { + z-index: 2; +} \ No newline at end of file diff --git a/front/style/player.css b/front/style/player.css index ebd0462..264b479 100644 --- a/front/style/player.css +++ b/front/style/player.css @@ -9,15 +9,11 @@ on the court. } .player-content { - /*apply a translation to center the player piece when placed*/ - transform: translate(-29%, -46%); - display: flex; flex-direction: column; align-content: center; align-items: center; outline: none; - } .player-piece { @@ -44,14 +40,18 @@ on the court. .player-selection-tab { display: flex; + + position: absolute; margin-bottom: 10%; justify-content: center; visibility: hidden; + + width: 100%; + transform: translateY(-20px); } .player-selection-tab-remove { pointer-events: all; - width: 25%; height: 25%; } diff --git a/front/views/Editor.tsx b/front/views/Editor.tsx index 8636cad..dba6dc2 100644 --- a/front/views/Editor.tsx +++ b/front/views/Editor.tsx @@ -7,27 +7,36 @@ import {BasketCourt} from "../components/editor/BasketCourt"; import {Rack} from "../components/Rack"; import {PlayerPiece} from "../components/editor/PlayerPiece"; import {Player} from "../data/Player"; +import {Team} from "../data/Team"; const ERROR_STYLE: CSSProperties = { borderColor: "red" } +/** + * information about a player that is into a rack + */ +interface RackedPlayer { + team: Team, + key: string, +} + export default function Editor({id, name}: { id: number, name: string }) { const [style, setStyle] = useState({}); const positions = ["1", "2", "3", "4", "5"] const [allies, setAllies] = useState( - positions.map(pos => ) + positions.map(key => ({team: Team.Allies, key})) ) const [opponents, setOpponents] = useState( - positions.map(pos => ) + positions.map(key => ({team: 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 +48,23 @@ export default function Editor({id, name}: { id: number, name: string }) { ); } - const onElementDetach = (ref: RefObject, element: ReactElement) => { - const refBounds = ref.current!.getBoundingClientRect(); + const onPieceDetach = (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, + role: element.key, + rightRatio: xRatio, + bottomRatio: yRatio }] }) } @@ -87,13 +96,17 @@ export default function Editor({id, name}: { id: number, name: string }) {
+ onElementDetached={onPieceDetach} + render={({team, key}) => }/> + onElementDetached={onPieceDetach} + render={({team, key}) => }/>
@@ -104,16 +117,23 @@ 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": + case Team.Opponents: setOpponents(opponents => ( - [...opponents, piece] + [...opponents, { + team: player.team, + pos: player.role, + key: player.role + }] )) break - case "allies": + case Team.Allies: setAllies(allies => ( - [...allies, piece] + [...allies, { + team: player.team, + pos: player.role, + key: player.role + }] )) } }}/> diff --git a/tsconfig.json b/tsconfig.json index 9da1fb5..d01f3cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "dom.iterable", "esnext" ], - "types": ["vite/client"], + "types": ["vite/client", "vite-plugin-svgr/client"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, diff --git a/vite.config.ts b/vite.config.ts index bb04351..4ff1dc5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -41,7 +41,7 @@ export default defineConfig({ relativeCSSInjection: true, }), svgr({ - include: "**/*.svg" + include: "**/*.svg?react" }) ] })