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";
export interface RackInput {
export interface RackProps<E> {
id: string,
objects: [ReactElement[], Dispatch<SetStateAction<ReactElement[]>>],
canDetach: (ref: RefObject<HTMLDivElement>) => boolean,
onElementDetached: (ref: RefObject<HTMLDivElement>, 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<HTMLDivElement>, el: ReactElement) => void
interface RackItemProps<E> {
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<E>({id, objects, onChange, canDetach, onElementDetached, render}: RackProps<E>) {
return (
<div id={id} style={{
display: "flex"
}}>
{rackObjects.map(element => (
{objects.map(element => (
<RackItem key={element.key}
item={element}
render={render}
onTryDetach={(ref, element) => {
if (!canDetach(ref))
return
setRackObjects(objects => {
const index = objects.findIndex(o => o.key === element.key)
return objects.toSpliced(index, 1);
})
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<E>({item, onTryDetach, render}: RackItemProps<E>) {
const divRef = useRef<HTMLDivElement>(null);
return (
<Draggable
position={{x: 0, y: 0}}
nodeRef={divRef}
onStop={() => onTryDetach(divRef, item)}>
onStop={() => onTryDetach(divRef.current!, item)}>
<div ref={divRef}>
{item}
{render(item)}
</div>
</Draggable>
)

@ -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
<CourtPlayer key={player.id}
pos={player.position}
team={player.team}
x={(bounds.width * player.right_percentage)}
y={(bounds.height * player.bottom_percentage)}
x={(bounds.width * player.rightRatio)}
y={(bounds.height * player.bottomRatio)}
bounds={{bottom: bounds.height, top: 0, left: 0, right: bounds.width}}
onRemove={() => onPlayerRemove(player)}
/>

@ -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<HTMLDivElement>(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}}
>
<div ref={ref}
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)
*/
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,
}

@ -12,22 +12,30 @@ const ERROR_STYLE: CSSProperties = {
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 }) {
const [style, setStyle] = useState<CSSProperties>({});
const positions = ["1", "2", "3", "4", "5"]
const [allies, setAllies] = useState(
positions.map(pos => <PlayerPiece team="allies" key={pos} text={pos}/>)
positions.map(key => ({team: "allies", key}))
)
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 courtDivContentRef = useRef<HTMLDivElement>(null);
const canDetach = (ref: RefObject<HTMLDivElement>) => {
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<HTMLDivElement>, 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 }) {
<div id="edit-div">
<div id="racks">
<Rack id="allies-rack"
objects={[allies, setAllies]}
objects={allies}
onChange={setAllies}
canDetach={canDetach}
onElementDetached={onElementDetach}/>
onElementDetached={onElementDetach}
render={({team, key}) => <PlayerPiece team={team} text={key} key={key}/>}/>
<Rack id="opponent-rack"
objects={[opponents, setOpponents]}
objects={opponents}
onChange={setOpponents}
canDetach={canDetach}
onElementDetached={onElementDetach}/>
onElementDetached={onElementDetach}
render={({team, key}) => <PlayerPiece team={team} text={key} key={key}/>}/>
</div>
<div id="court-div">
<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)
return players.toSpliced(idx, 1)
})
const piece = <PlayerPiece team={player.team} key={player.position} text={player.position}/>
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}]
))
}
}}/>

Loading…
Cancel
Save