apply suggestions
continuous-integration/drone/push Build is passing Details

pull/40/head
maxime.batista 1 year ago
parent ef80aa3192
commit a6cceb31ea

@ -1,40 +1,39 @@
import {Dispatch, ReactElement, RefObject, SetStateAction, useRef} from "react"; import {ReactElement, useRef} from "react";
import Draggable from "react-draggable"; import Draggable from "react-draggable";
export interface RackInput { export interface RackProps<E> {
id: string, id: string,
objects: [ReactElement[], Dispatch<SetStateAction<ReactElement[]>>], objects: E[],
canDetach: (ref: RefObject<HTMLDivElement>) => boolean, onChange: (objects: E[]) => void,
onElementDetached: (ref: RefObject<HTMLDivElement>, el: ReactElement) => void, canDetach: (ref: HTMLDivElement) => boolean,
onElementDetached: (ref: HTMLDivElement, el: E) => void,
render: (e: E) => ReactElement,
} }
interface RackItemInput { interface RackItemProps<E> {
item: ReactElement, item: E,
onTryDetach: (ref: RefObject<HTMLDivElement>, el: ReactElement) => void onTryDetach: (ref: HTMLDivElement, el: E) => void,
render: (e: E) => ReactElement,
} }
/** /**
* A container of draggable objects * A container of draggable objects
* */ * */
export function Rack({id, objects, canDetach, onElementDetached}: RackInput) { export function Rack<E>({id, objects, onChange, canDetach, onElementDetached, render}: RackProps<E>) {
const [rackObjects, setRackObjects] = objects
return ( return (
<div id={id} style={{ <div id={id} style={{
display: "flex" display: "flex"
}}> }}>
{rackObjects.map(element => ( {objects.map(element => (
<RackItem key={element.key} <RackItem key={element.key}
item={element} item={element}
render={render}
onTryDetach={(ref, element) => { onTryDetach={(ref, element) => {
if (!canDetach(ref)) if (!canDetach(ref))
return return
setRackObjects(objects => {
const index = objects.findIndex(o => o.key === element.key) const index = objects.findIndex(o => o.key === element.key)
return objects.toSpliced(index, 1); onChange(objects.toSpliced(index, 1))
})
onElementDetached(ref, element) onElementDetached(ref, element)
}}/> }}/>
@ -43,16 +42,16 @@ export function Rack({id, objects, canDetach, onElementDetached}: RackInput) {
) )
} }
function RackItem({item, onTryDetach}: RackItemInput) { function RackItem<E>({item, onTryDetach, render}: RackItemProps<E>) {
const divRef = useRef<HTMLDivElement>(null); const divRef = useRef<HTMLDivElement>(null);
return ( return (
<Draggable <Draggable
position={{x: 0, y: 0}} position={{x: 0, y: 0}}
nodeRef={divRef} nodeRef={divRef}
onStop={() => onTryDetach(divRef, item)}> onStop={() => onTryDetach(divRef.current!, item)}>
<div ref={divRef}> <div ref={divRef}>
{item} {render(item)}
</div> </div>
</Draggable> </Draggable>
) )

@ -1,6 +1,6 @@
import CourtSvg from '../../assets/basketball_court.svg'; import CourtSvg from '../../assets/basketball_court.svg';
import '../../style/basket_court.css'; 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 CourtPlayer from "./CourtPlayer";
import {Player} from "../../data/Player"; import {Player} from "../../data/Player";
@ -15,8 +15,8 @@ export function BasketCourt({players, onPlayerRemove}: { players: Player[], onPl
<CourtPlayer key={player.id} <CourtPlayer key={player.id}
pos={player.position} pos={player.position}
team={player.team} team={player.team}
x={(bounds.width * player.right_percentage)} x={(bounds.width * player.rightRatio)}
y={(bounds.height * player.bottom_percentage)} y={(bounds.height * player.bottomRatio)}
bounds={{bottom: bounds.height, top: 0, left: 0, right: bounds.width}} bounds={{bottom: bounds.height, top: 0, left: 0, right: bounds.width}}
onRemove={() => onPlayerRemove(player)} onRemove={() => onPlayerRemove(player)}
/> />

@ -1,10 +1,10 @@
import React, {useRef} from "react"; import {useRef} from "react";
import "../../style/player.css"; import "../../style/player.css";
import RemoveIcon from "../../assets/icon/remove.svg"; import RemoveIcon from "../../assets/icon/remove.svg";
import Draggable, {DraggableBounds} from "react-draggable"; import Draggable, {DraggableBounds} from "react-draggable";
import {PlayerPiece} from "./PlayerPiece"; import {PlayerPiece} from "./PlayerPiece";
export interface PlayerOptions { export interface PlayerProps {
pos: string, pos: string,
team: string, team: string,
x: number, 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 * 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<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
return ( return (
@ -24,7 +24,7 @@ export default function CourtPlayer({pos, team, x, y, bounds, onRemove}: PlayerO
handle={".player-piece"} handle={".player-piece"}
nodeRef={ref} nodeRef={ref}
bounds={bounds} bounds={bounds}
defaultPosition={{x: x, y: y}} defaultPosition={{x, y}}
> >
<div ref={ref} <div ref={ref}
className={"player"} className={"player"}

@ -18,10 +18,10 @@ export interface Player {
/** /**
* Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) * 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) * Percentage of the player's position to the right (0 means left, 1 means right, 0.5 means middle)
*/ */
right_percentage: number, rightRatio: number,
} }

@ -12,22 +12,30 @@ const ERROR_STYLE: CSSProperties = {
borderColor: "red" borderColor: "red"
} }
/**
* information about a player that is into a rack
*/
interface RackedPlayer {
team: "allies" | "opponents",
key: string,
}
export default function Editor({id, name}: { id: number, name: string }) { export default function Editor({id, name}: { id: number, name: string }) {
const [style, setStyle] = useState<CSSProperties>({}); const [style, setStyle] = useState<CSSProperties>({});
const positions = ["1", "2", "3", "4", "5"] const positions = ["1", "2", "3", "4", "5"]
const [allies, setAllies] = useState( const [allies, setAllies] = useState(
positions.map(pos => <PlayerPiece team="allies" key={pos} text={pos}/>) positions.map(key => ({team: "allies", key}))
) )
const [opponents, setOpponents] = useState( const [opponents, setOpponents] = useState(
positions.map(pos => <PlayerPiece team="opponents" key={pos} text={pos}/>) positions.map(key => ({team: "opponents", key}))
) )
const [players, setPlayers] = useState<Player[]>([]); const [players, setPlayers] = useState<Player[]>([]);
const courtDivContentRef = useRef<HTMLDivElement>(null); const courtDivContentRef = useRef<HTMLDivElement>(null);
const canDetach = (ref: RefObject<HTMLDivElement>) => { const canDetach = (ref: HTMLDivElement) => {
const refBounds = ref.current!.getBoundingClientRect(); const refBounds = ref.getBoundingClientRect();
const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); const courtBounds = courtDivContentRef.current!.getBoundingClientRect();
// check if refBounds overlaps courtBounds // check if refBounds overlaps courtBounds
@ -39,23 +47,23 @@ export default function Editor({id, name}: { id: number, name: string }) {
); );
} }
const onElementDetach = (ref: RefObject<HTMLDivElement>, element: ReactElement) => { const onElementDetach = (ref: HTMLDivElement, element: RackedPlayer) => {
const refBounds = ref.current!.getBoundingClientRect(); const refBounds = ref.getBoundingClientRect();
const courtBounds = courtDivContentRef.current!.getBoundingClientRect(); const courtBounds = courtDivContentRef.current!.getBoundingClientRect();
const relativeXPixels = refBounds.x - courtBounds.x; const relativeXPixels = refBounds.x - courtBounds.x;
const relativeYPixels = refBounds.y - courtBounds.y; const relativeYPixels = refBounds.y - courtBounds.y;
const xPercent = relativeXPixels / courtBounds.width; const xRatio = relativeXPixels / courtBounds.width;
const yPercent = relativeYPixels / courtBounds.height; const yRatio = relativeYPixels / courtBounds.height;
setPlayers(players => { setPlayers(players => {
return [...players, { return [...players, {
id: players.length, id: players.length,
team: element.props.team, team: element.team,
position: element.props.text, position: element.key,
right_percentage: xPercent, rightRatio: xRatio,
bottom_percentage: yPercent bottomRatio: yRatio
}] }]
}) })
} }
@ -87,13 +95,17 @@ export default function Editor({id, name}: { id: number, name: string }) {
<div id="edit-div"> <div id="edit-div">
<div id="racks"> <div id="racks">
<Rack id="allies-rack" <Rack id="allies-rack"
objects={[allies, setAllies]} objects={allies}
onChange={setAllies}
canDetach={canDetach} canDetach={canDetach}
onElementDetached={onElementDetach}/> onElementDetached={onElementDetach}
render={({team, key}) => <PlayerPiece team={team} text={key} key={key}/>}/>
<Rack id="opponent-rack" <Rack id="opponent-rack"
objects={[opponents, setOpponents]} objects={opponents}
onChange={setOpponents}
canDetach={canDetach} canDetach={canDetach}
onElementDetached={onElementDetach}/> onElementDetached={onElementDetach}
render={({team, key}) => <PlayerPiece team={team} text={key} key={key}/>}/>
</div> </div>
<div id="court-div"> <div id="court-div">
<div id="court-div-bounds" ref={courtDivContentRef}> <div id="court-div-bounds" ref={courtDivContentRef}>
@ -104,16 +116,15 @@ export default function Editor({id, name}: { id: number, name: string }) {
const idx = players.indexOf(player) const idx = players.indexOf(player)
return players.toSpliced(idx, 1) return players.toSpliced(idx, 1)
}) })
const piece = <PlayerPiece team={player.team} key={player.position} text={player.position}/>
switch (player.team) { switch (player.team) {
case "opponents": case "opponents":
setOpponents(opponents => ( setOpponents(opponents => (
[...opponents, piece] [...opponents, {team: player.team, pos: player.position, key: player.position}]
)) ))
break break
case "allies": case "allies":
setAllies(allies => ( setAllies(allies => (
[...allies, piece] [...allies, {team: player.team, pos: player.position, key: player.position}]
)) ))
} }
}}/> }}/>

Loading…
Cancel
Save