import { CourtBall } from "./CourtBall" import { ReactElement, RefObject, useCallback, useLayoutEffect, 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 BendableArrow, { Segment } from "../arrows/BendableArrow" import { middlePos, NULL_POS, Pos, ratioWithinBase } from "../arrows/Pos" import BallAction from "../actions/BallAction" import { CourtObject } from "../../tactic/CourtObjects" export interface BasketCourtProps { players: Player[] actions: Action[] objects: CourtObject[] renderAction: (a: Action, key: number) => ReactElement setActions: (f: (a: Action[]) => Action[]) => void onPlayerRemove: (p: Player) => void onPlayerChange: (p: Player) => void onBallRemove: () => void onBallMoved: (ball: DOMRect) => void courtImage: () => ReactElement courtRef: RefObject } export function BasketCourt({ players, actions, objects, renderAction, setActions, onPlayerRemove, onPlayerChange, onBallMoved, onBallRemove, courtImage, courtRef, }: BasketCourtProps) { function placeArrow(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 targetPos = document .getElementById(player.id)! .getBoundingClientRect() const courtBounds = courtRef.current!.getBoundingClientRect() const start = ratioWithinBase( middlePos(originRef.getBoundingClientRect()), courtBounds, ) const end = ratioWithinBase(middlePos(targetPos), courtBounds) const action: Action = { fromPlayerId: originRef.id, toPlayerId: player.id, type: MovementActionKind.SCREEN, moveFrom: start, segments: [{ next: end }], } setActions((actions) => [...actions, action]) return } } const action: Action = { fromPlayerId: originRef.id, type: MovementActionKind.MOVE, moveFrom: middlePos(originRef.getBoundingClientRect()), segments: [{ next: middlePos(arrowHead) }], } setActions((actions) => [...actions, action]) } const [previewArrowOriginPos, setPreviewArrowOriginPos] = useState(NULL_POS) const [isPreviewArrowEnabled, setPreviewArrowEnabled] = useState(false) const [previewArrowEdges, setPreviewArrowEdges] = useState([]) const updateActionsRelatedTo = useCallback((player: Player) => { const newPos = ratioWithinBase( middlePos( document.getElementById(player.id)!.getBoundingClientRect(), ), courtRef.current!.getBoundingClientRect(), ) setActions((actions) => actions.map((a) => { if (a.fromPlayerId == player.id) { return { ...a, moveFrom: newPos } } if (a.toPlayerId == player.id) { const segments = a.segments.toSpliced( a.segments.length - 1, 1, { ...a.segments[a.segments.length - 1], next: newPos, }, ) return { ...a, segments } } return a }), ) }, []) const [internActions, setInternActions] = useState([]) useLayoutEffect(() => setInternActions(actions), [actions]) return (
{courtImage()} {internActions.map((action, idx) => renderAction(action, idx))} {players.map((player) => ( updateActionsRelatedTo(player)} onChange={onPlayerChange} onRemove={() => onPlayerRemove(player)} parentRef={courtRef} availableActions={(pieceRef) => [ onPlayerRemove(player)} />, { const baseBounds = courtRef.current!.getBoundingClientRect() setPreviewArrowEdges([ { next: ratioWithinBase( middlePos(headPos), baseBounds, ), }, ]) }} onHeadPicked={(headPos) => { const baseBounds = courtRef.current!.getBoundingClientRect() setPreviewArrowOriginPos( ratioWithinBase( middlePos( pieceRef.getBoundingClientRect(), ), baseBounds, ), ) setPreviewArrowEdges([ { next: ratioWithinBase( middlePos(headPos), baseBounds, ), }, ]) setPreviewArrowEnabled(true) }} onHeadDropped={(headRect) => { placeArrow(pieceRef, headRect) setPreviewArrowEnabled(false) }} />, player.hasBall && ( onBallMoved(ref.getBoundingClientRect()) } /> ), ]} /> ))} {objects.map((object) => { if (object.type == "ball") { return ( ) } throw new Error("unknown court object", object.type) })} {isPreviewArrowEnabled && ( {}} //TODO place those values in constants endRadius={17} startRadius={26} /> )}
) }