import { CourtBall } from "./CourtBall" import { ReactElement, RefObject, useCallback, useLayoutEffect, useState, } from "react" import CourtPlayer from "./CourtPlayer" import { Player } from "../../model/tactic/Player" import { Action, ActionKind } from "../../model/tactic/Action" import ArrowAction from "../actions/ArrowAction" import { middlePos, ratioWithinBase } from "../../geo/Pos" import BallAction from "../actions/BallAction" import {BALL_ID} from "../../model/tactic/Ball" import { contains, overlaps } from "../../geo/Box" import { CourtAction } from "../../views/editor/CourtAction" import { TacticComponent } from "../../model/tactic/Tactic" export interface BasketCourtProps { components: TacticComponent[] actions: Action[] 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({ components, actions, renderAction, setActions, onPlayerRemove, onPlayerChange, onBallMoved, onBallRemove, courtImage, courtRef, }: BasketCourtProps) { function placeArrow(origin: Player, arrowHead: DOMRect) { const originRef = document.getElementById(origin.id)! const courtBounds = courtRef.current!.getBoundingClientRect() const start = ratioWithinBase( middlePos(originRef.getBoundingClientRect()), courtBounds, ) for (const component of components) { if (component.id == origin.id) { continue } const playerBounds = document .getElementById(component.id)! .getBoundingClientRect() if (overlaps(playerBounds, arrowHead)) { const targetPos = document .getElementById(component.id)! .getBoundingClientRect() const end = ratioWithinBase(middlePos(targetPos), courtBounds) const action: Action = { fromId: originRef.id, toId: component.id, type: component.type == "player" ? origin.hasBall ? ActionKind.SHOOT : ActionKind.SCREEN : ActionKind.MOVE, moveFrom: start, segments: [{ next: end }], } setActions((actions) => [...actions, action]) return } } const action: Action = { fromId: originRef.id, type: origin.hasBall ? ActionKind.DRIBBLE : ActionKind.MOVE, moveFrom: ratioWithinBase( middlePos(originRef.getBoundingClientRect()), courtBounds, ), segments: [ { next: ratioWithinBase(middlePos(arrowHead), courtBounds) }, ], } setActions((actions) => [...actions, action]) } const [previewAction, setPreviewAction] = useState(null) const updateActionsRelatedTo = useCallback((comp: TacticComponent) => { const newPos = ratioWithinBase( middlePos( document.getElementById(comp.id)!.getBoundingClientRect(), ), courtRef.current!.getBoundingClientRect(), ) setActions((actions) => actions.map((a) => { if (a.fromId == comp.id) { return { ...a, moveFrom: newPos } } if (a.toId == comp.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} {components.map((component) => { if (component.type == "player") { const player = component return ( updateActionsRelatedTo(player)} onChange={onPlayerChange} onRemove={() => onPlayerRemove(player)} courtRef={courtRef} availableActions={(pieceRef) => [ { const baseBounds = courtRef.current!.getBoundingClientRect() const arrowHeadPos = middlePos(headPos) const target = components.find( (c) => c.id != player.id && contains( document .getElementById(c.id)! .getBoundingClientRect(), arrowHeadPos, ), ) const type = target?.type == "player" ? player.hasBall ? target ? ActionKind.SHOOT : ActionKind.DRIBBLE : target ? ActionKind.SCREEN : ActionKind.MOVE : ActionKind.MOVE setPreviewAction((action) => ({ ...action!, segments: [ { next: ratioWithinBase( arrowHeadPos, baseBounds, ), }, ], type, })) }} onHeadPicked={(headPos) => { ;( document.activeElement as HTMLElement ).blur() const baseBounds = courtRef.current!.getBoundingClientRect() setPreviewAction({ type: player.hasBall ? ActionKind.DRIBBLE : ActionKind.MOVE, fromId: player.id, toId: undefined, moveFrom: ratioWithinBase( middlePos( pieceRef.getBoundingClientRect(), ), baseBounds, ), segments: [ { next: ratioWithinBase( middlePos(headPos), baseBounds, ), }, ], }) }} onHeadDropped={(headRect) => { placeArrow(player, headRect) setPreviewAction(null) }} />, player.hasBall && ( onBallMoved( ref.getBoundingClientRect(), ) } /> ), ]} /> ) } if (component.type == BALL_ID) { return ( updateActionsRelatedTo(component)} ball={component} onRemove={onBallRemove} key="ball" /> ) } throw new Error("unknown tactic component " + component) })} {internActions.map((action, idx) => renderAction(action, idx))} {previewAction && ( {}} onActionChanges={() => {}} /> )}
) }