You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Application-Web/front/components/editor/BasketCourt.tsx

150 lines
5.4 KiB

import "../../style/basket_court.css"
import {ReactElement, RefObject, useState} from "react"
import CourtPlayer from "./CourtPlayer"
import {Player} from "../../tactic/Player"
import {Action, MovementActionKind} from "../../tactic/Action"
import RemoveAction from "../actions/RemoveAction"
import ArrowAction from "../actions/ArrowAction"
import {useXarrow} from "react-xarrows"
import BendableArrow from "../arrows/BendableArrow"
import {middlePos, NULL_POS, Pos} from "../arrows/Pos"
import {CourtObject} from "../../tactic/CourtObjects";
import {CourtBall} from "./CourtBall";
import BallAction from "../actions/BallAction";
export interface BasketCourtProps {
players: Player[]
actions: Action[]
objects: CourtObject[]
renderAction: (courtBounds: DOMRect, a: Action, idx: number) => ReactElement
setActions: (f: (a: Action[]) => Action[]) => void
onPlayerRemove: (p: Player) => void
onPlayerChange: (p: Player) => void
onBallRemove: () => void
onBallMoved: (ball: DOMRect) => void
courtImage: string
courtRef: RefObject<HTMLDivElement>
}
export function BasketCourt({
objects,
onBallMoved,
onBallRemove,
players,
actions,
renderAction,
setActions,
onPlayerRemove,
onPlayerChange,
courtImage,
courtRef,
}: BasketCourtProps) {
function bindArrowToPlayer(originRef: HTMLElement, arrowHead: DOMRect) {
for (const player of players) {
if (player.id == originRef.id) {
continue
}
const playerBounds = document
.getElementById(player.id)!
.getBoundingClientRect()
if (
!(
playerBounds.top > arrowHead.bottom ||
playerBounds.right < arrowHead.left ||
playerBounds.bottom < arrowHead.top ||
playerBounds.left > arrowHead.right
)
) {
const action = {
type: MovementActionKind.SCREEN,
moveFrom: originRef.id,
moveTo: player.id,
}
setActions((actions) => [...actions, action])
}
}
}
const updateArrows = useXarrow()
const [previewArrowOriginPos, setPreviewArrowOriginPos] =
useState<Pos>(NULL_POS)
const [previewArrowEndPos, setPreviewArrowEndPos] = useState<Pos>(NULL_POS)
const [isPreviewArrowEnabled, setPreviewArrowEnabled] = useState(false)
return (
<div id="court-container" ref={courtRef} style={{position: "relative"}}>
<img src={courtImage} alt={"court"} id="court-svg"/>
{actions.map((action, idx) => renderAction(courtRef.current!.getBoundingClientRect(), action, idx))}
{players.map((player) => (
<CourtPlayer
key={player.id}
player={player}
onDrag={updateArrows}
onChange={onPlayerChange}
onRemove={() => onPlayerRemove(player)}
parentRef={courtRef}
availableActions={(pieceRef) => [
<RemoveAction
key={1}
onRemove={() => onPlayerRemove(player)}
/>,
<ArrowAction
key={2}
onHeadMoved={(headPos) =>
setPreviewArrowEndPos(middlePos(headPos))
}
onHeadPicked={(headRef) => {
setPreviewArrowOriginPos(
middlePos(pieceRef.getBoundingClientRect()),
)
setPreviewArrowEndPos(middlePos(headRef))
setPreviewArrowEnabled(true)
}}
onHeadDropped={(headRect) => {
setPreviewArrowEnabled(false)
bindArrowToPlayer(pieceRef, headRect)
}}
/>,
player.hasBall && <BallAction key={3} onDrop={ref => onBallMoved(ref.getBoundingClientRect())}/>
]}
/>
))}
{objects.map((object) => {
if (object.type == "ball") {
return (
<CourtBall
onMoved={onBallMoved}
ball={object}
onRemove={onBallRemove}
key="ball"
/>
)
}
throw new Error("unknown court object", object.type)
})}
{isPreviewArrowEnabled && (
<BendableArrow
basePos={courtRef.current!.getBoundingClientRect()}
startPos={previewArrowOriginPos}
endPos={previewArrowEndPos}
/>
)}
</div>
)
}