diff --git a/src/components/editor/BasketCourt.tsx b/src/components/editor/BasketCourt.tsx index 4cab8cf..25b53b5 100644 --- a/src/components/editor/BasketCourt.tsx +++ b/src/components/editor/BasketCourt.tsx @@ -1,8 +1,14 @@ -import {ReactElement, ReactNode, RefObject, useLayoutEffect, useState} from "react" -import {Action} from "../../model/tactic/Action" +import { + ReactElement, + ReactNode, + RefObject, + useLayoutEffect, + useState, +} from "react" +import { Action } from "../../model/tactic/Action" -import {CourtAction} from "./CourtAction.tsx" -import {ComponentId, TacticComponent} from "../../model/tactic/Tactic" +import { CourtAction } from "./CourtAction.tsx" +import { ComponentId, TacticComponent } from "../../model/tactic/Tactic" export interface BasketCourtProps { components: TacticComponent[] @@ -21,21 +27,20 @@ export interface ActionPreview extends Action { } export function BasketCourt({ - components, - previewAction, + components, + previewAction, - renderComponent, - renderActions, - - courtImage, - courtRef, - }: BasketCourtProps) { + renderComponent, + renderActions, + courtImage, + courtRef, +}: BasketCourtProps) { const [forceEmptyComponents, setForceEmptyComponents] = useState(true) useLayoutEffect(() => { setForceEmptyComponents(false) - }, [setForceEmptyComponents]); + }, [setForceEmptyComponents]) const usedComponents = forceEmptyComponents ? [] : components @@ -43,7 +48,7 @@ export function BasketCourt({
+ style={{ position: "relative" }}> {courtImage} {usedComponents.map(renderComponent)} @@ -56,10 +61,8 @@ export function BasketCourt({ origin={previewAction.origin} isInvalid={previewAction.isInvalid} //do nothing on interacted, not really possible as it's a preview arrow - onActionDeleted={() => { - }} - onActionChanges={() => { - }} + onActionDeleted={() => {}} + onActionChanges={() => {}} /> )}
diff --git a/src/components/editor/CourtBall.tsx b/src/components/editor/CourtBall.tsx index e368598..c9c48dc 100644 --- a/src/components/editor/CourtBall.tsx +++ b/src/components/editor/CourtBall.tsx @@ -1,8 +1,8 @@ -import {useRef} from "react" +import { useRef } from "react" import Draggable from "react-draggable" -import {BallPiece} from "./BallPiece" -import {NULL_POS} from "../../geo/Pos" -import {Ball} from "../../model/tactic/CourtObjects" +import { BallPiece } from "./BallPiece" +import { NULL_POS } from "../../geo/Pos" +import { Ball } from "../../model/tactic/CourtObjects" export interface CourtBallProps { onPosValidated: (rect: DOMRect) => void @@ -10,10 +10,10 @@ export interface CourtBallProps { ball: Ball } -export function CourtBall({onPosValidated, ball, onRemove}: CourtBallProps) { +export function CourtBall({ onPosValidated, ball, onRemove }: CourtBallProps) { const pieceRef = useRef(null) - const {x, y} = ball.pos + const { x, y } = ball.pos return ( - + ) diff --git a/src/components/editor/CourtPlayer.tsx b/src/components/editor/CourtPlayer.tsx index 2f8e5cb..6b8e8dd 100644 --- a/src/components/editor/CourtPlayer.tsx +++ b/src/components/editor/CourtPlayer.tsx @@ -1,9 +1,9 @@ -import React, {ReactNode, RefObject, useCallback, useRef} from "react" +import React, { ReactNode, RefObject, useCallback, useRef } from "react" import "../../style/player.css" import Draggable from "react-draggable" -import {PlayerPiece} from "./PlayerPiece" -import {BallState, PlayerInfo} from "../../model/tactic/Player" -import {NULL_POS, Pos, ratioWithinBase} from "../../geo/Pos" +import { PlayerPiece } from "./PlayerPiece" +import { BallState, PlayerInfo } from "../../model/tactic/Player" +import { NULL_POS, Pos, ratioWithinBase } from "../../geo/Pos" export interface CourtPlayerProps { playerInfo: PlayerInfo @@ -23,16 +23,16 @@ export const PLAYER_RADIUS_PIXELS = 20 * A player that is placed on the court, which can be selected, and moved in the associated bounds * */ export default function CourtPlayer({ - playerInfo, - className, + playerInfo, + className, - onPositionValidated, - onRemove, - courtRef, - availableActions, - }: CourtPlayerProps) { + onPositionValidated, + onRemove, + courtRef, + availableActions, +}: CourtPlayerProps) { const usesBall = playerInfo.ballState != BallState.NONE - const {x, y} = playerInfo.pos + const { x, y } = playerInfo.pos const pieceRef = useRef(null) return ( @@ -47,8 +47,11 @@ export default function CourtPlayer({ const pos = ratioWithinBase(pieceBounds, parentBounds) - - if (Math.abs(pos.x - x) >= MOVE_AREA_SENSIBILITY || Math.abs(pos.y - y) >= MOVE_AREA_SENSIBILITY) onPositionValidated(pos) + if ( + Math.abs(pos.x - x) >= MOVE_AREA_SENSIBILITY || + Math.abs(pos.y - y) >= MOVE_AREA_SENSIBILITY + ) + onPositionValidated(pos) }, [courtRef, onPositionValidated, x, y])}>
c.id == pathItem.originPlayerId)! as Player } -export function getPlayerNextTo(player: PlayerLike, n: number, components: TacticComponent[]): PlayerLike | undefined { - const playerOrigin = player.type === "player" ? player : getOrigin(player, components) +export function getPlayerNextTo( + player: PlayerLike, + n: number, + components: TacticComponent[], +): PlayerLike | undefined { + const playerOrigin = + player.type === "player" ? player : getOrigin(player, components) const pathItems = playerOrigin.path?.items! // add one as there is a shifting because a Player is never at the head of its own path @@ -24,20 +49,24 @@ export function getPlayerNextTo(player: PlayerLike, n: number, components: Tacti const targetIdx = idx + n // remove the screen phantom - const result = targetIdx == 0 ? playerOrigin : getComponent(pathItems[targetIdx - 1], components) + const result = + targetIdx == 0 + ? playerOrigin + : getComponent(pathItems[targetIdx - 1], components) return result } //FIXME this function can be a bottleneck if the phantom's position is // following another phantom and / or the origin of the phantom is another -export function computePhantomPositioning(phantom: PlayerPhantom, - content: TacticContent, - area: DOMRect): Pos { +export function computePhantomPositioning( + phantom: PlayerPhantom, + content: TacticContent, + area: DOMRect, +): Pos { const positioning = phantom.pos // If the position is already known and fixed, return the pos - if (positioning.type === "fixed") - return positioning + if (positioning.type === "fixed") return positioning // If the position is to determine (positioning.type = "follows"), determine the phantom's pos // by calculating it from the referent position, and the action that targets the referent. @@ -46,44 +75,56 @@ export function computePhantomPositioning(phantom: PlayerPhantom, // Get the referent from the components const referent: PlayerLike = getComponent(positioning.attach, components) - const referentPos = referent.type === "player" - ? referent.pos - : computePhantomPositioning(referent, content, area) + const referentPos = + referent.type === "player" + ? referent.pos + : computePhantomPositioning(referent, content, area) // Get the origin const origin = getOrigin(phantom, components) const originPathItems = origin.path!.items const phantomIdx = originPathItems.indexOf(phantom.id) - const playerBeforePhantom: PlayerLike = phantomIdx == 0 ? origin : getComponent(originPathItems[phantomIdx - 1], components) - const action = playerBeforePhantom.actions.find(a => a.target === positioning.attach)! + const playerBeforePhantom: PlayerLike = + phantomIdx == 0 + ? origin + : getComponent(originPathItems[phantomIdx - 1], components) + const action = playerBeforePhantom.actions.find( + (a) => a.target === positioning.attach, + )! const segments = action.segments const lastSegment = segments[segments.length - 1] const lastSegmentStart = segments[segments.length - 2]?.next - const pivotPoint = lastSegment.controlPoint ?? (lastSegmentStart - ? typeof lastSegmentStart === "string" - ? document.getElementById(lastSegmentStart)!.getBoundingClientRect() - : lastSegmentStart - : playerBeforePhantom.type === "phantom" - ? computePhantomPositioning(playerBeforePhantom, content, area) - : playerBeforePhantom.pos) - + const pivotPoint = + lastSegment.controlPoint ?? + (lastSegmentStart + ? typeof lastSegmentStart === "string" + ? document + .getElementById(lastSegmentStart)! + .getBoundingClientRect() + : lastSegmentStart + : playerBeforePhantom.type === "phantom" + ? computePhantomPositioning(playerBeforePhantom, content, area) + : playerBeforePhantom.pos) const segment = posWithinBase(relativeTo(referentPos, pivotPoint), area) const segmentLength = norm(segment) const phantomDistanceFromReferent = PLAYER_RADIUS_PIXELS //TODO Place this in constants const segmentProjection = minus(area, { x: (segment.x / segmentLength) * phantomDistanceFromReferent, - y: (segment.y / segmentLength) * phantomDistanceFromReferent + y: (segment.y / segmentLength) * phantomDistanceFromReferent, }) const segmentProjectionRatio: Pos = ratioWithinBase(segmentProjection, area) return add(referentPos, segmentProjectionRatio) } -export function getComponent(id: string, components: TacticComponent[]): T { - return components.find(c => c.id === id)! as T +export function getComponent( + id: string, + components: TacticComponent[], +): T { + return components.find((c) => c.id === id)! as T } export function areInSamePath(a: PlayerLike, b: PlayerLike) { @@ -141,13 +182,18 @@ export function clearPlayerPath( ) } -function removeAllPhantomsAttached(to: ComponentId, content: TacticContent): TacticContent { +function removeAllPhantomsAttached( + to: ComponentId, + content: TacticContent, +): TacticContent { let i = 0 while (i < content.components.length) { const component = content.components[i] if (component.type === "phantom") { - - if (component.pos.type === "follows" && component.pos.attach === to) { + if ( + component.pos.type === "follows" && + component.pos.attach === to + ) { content = removePlayer(component, content) continue } @@ -165,16 +211,24 @@ export function removePlayer( content = removeAllPhantomsAttached(player.id, content) if (player.type === "phantom") { - const pos = player.pos // if the phantom was attached to another player, remove the action that symbolizes the attachment if (pos.type === "follows") { - const playerBefore = getPlayerNextTo(player, -1, content.components)! - const actionIdx = playerBefore.actions.findIndex(a => a.target === pos.attach) - content = updateComponent({ - ...playerBefore, - actions: playerBefore.actions.toSpliced(actionIdx, 1) - }, content) + const playerBefore = getPlayerNextTo( + player, + -1, + content.components, + )! + const actionIdx = playerBefore.actions.findIndex( + (a) => a.target === pos.attach, + ) + content = updateComponent( + { + ...playerBefore, + actions: playerBefore.actions.toSpliced(actionIdx, 1), + }, + content, + ) } const origin = getOrigin(player, content.components) @@ -229,9 +283,9 @@ export function truncatePlayerPath( truncateStartIdx == 0 ? null : { - ...path, - items: path.items.toSpliced(truncateStartIdx), - }, + ...path, + items: path.items.toSpliced(truncateStartIdx), + }, }, content, ) diff --git a/src/editor/TacticContentDomains.ts b/src/editor/TacticContentDomains.ts index a8d02d3..1f3a9cc 100644 --- a/src/editor/TacticContentDomains.ts +++ b/src/editor/TacticContentDomains.ts @@ -1,11 +1,26 @@ -import {Pos, ratioWithinBase} from "../geo/Pos" -import {BallState, Player, PlayerInfo, PlayerLike, PlayerTeam,} from "../model/tactic/Player" -import {Ball, BALL_ID, BALL_TYPE, CourtObject,} from "../model/tactic/CourtObjects" -import {ComponentId, TacticComponent, TacticContent,} from "../model/tactic/Tactic" -import {overlaps} from "../geo/Box" -import {RackedCourtObject, RackedPlayer} from "./RackedItems" -import {changePlayerBallState, getComponent, getOrigin} from "./PlayerDomains" -import {ActionKind} from "../model/tactic/Action.ts"; +import { Pos, ratioWithinBase } from "../geo/Pos" +import { + BallState, + Player, + PlayerInfo, + PlayerLike, + PlayerTeam, +} from "../model/tactic/Player" +import { + Ball, + BALL_ID, + BALL_TYPE, + CourtObject, +} from "../model/tactic/CourtObjects" +import { + ComponentId, + TacticComponent, + TacticContent, +} from "../model/tactic/Tactic" +import { overlaps } from "../geo/Box" +import { RackedCourtObject, RackedPlayer } from "./RackedItems" +import { changePlayerBallState, getComponent, getOrigin } from "./PlayerDomains" +import { ActionKind } from "../model/tactic/Action.ts" export function placePlayerAt( refBounds: DOMRect, @@ -167,20 +182,37 @@ export function moveComponent( const originPathItems = origin.path!.items const phantomIdx = originPathItems.indexOf(component.id) - const playerBeforePhantom: PlayerLike = phantomIdx == 0 ? origin : getComponent(originPathItems[phantomIdx - 1], content.components) + const playerBeforePhantom: PlayerLike = + phantomIdx == 0 + ? origin + : getComponent( + originPathItems[phantomIdx - 1], + content.components, + ) // detach the action from the screen target and transform it to a regular move action to the phantom. - content = updateComponent({ - ...playerBeforePhantom, - actions: playerBeforePhantom.actions.map(a => a.target === referent ? { - ...a, - segments: a.segments.toSpliced(a.segments.length - 2, 1, { - ...a.segments[a.segments.length - 1], - next: component.id, - }), - target: component.id, - type: ActionKind.MOVE - } : a), - }, content) + content = updateComponent( + { + ...playerBeforePhantom, + actions: playerBeforePhantom.actions.map((a) => + a.target === referent + ? { + ...a, + segments: a.segments.toSpliced( + a.segments.length - 2, + 1, + { + ...a.segments[a.segments.length - 1], + next: component.id, + }, + ), + target: component.id, + type: ActionKind.MOVE, + } + : a, + ), + }, + content, + ) } content = updateComponent( @@ -188,10 +220,10 @@ export function moveComponent( ...component, pos: isPhantom ? { - type: "fixed", - ...newPos - } - : newPos + type: "fixed", + ...newPos, + } + : newPos, }, content, ) @@ -253,5 +285,5 @@ export function getRackPlayers( c.type == "player" && c.team == team && c.role == role, ) == -1, ) - .map((key) => ({team, key})) + .map((key) => ({ team, key })) } diff --git a/src/model/tactic/Player.ts b/src/model/tactic/Player.ts index ab4d116..a22eaee 100644 --- a/src/model/tactic/Player.ts +++ b/src/model/tactic/Player.ts @@ -34,7 +34,7 @@ export interface PlayerInfo { */ readonly ballState: BallState - readonly pos: Pos, + readonly pos: Pos } export enum BallState { @@ -61,24 +61,26 @@ export interface MovementPath { /** * The position of the phantom is known and fixed */ -export type FixedPhantomPositioning = ({ type: "fixed" } & Pos) +export type FixedPhantomPositioning = { type: "fixed" } & Pos /** * The position of the phantom is constrained to a given component. * The actual position of the phantom is to determine given its environment. */ -export type FollowsPhantomPositioning = { type: "follows", attach: ComponentId } +export type FollowsPhantomPositioning = { type: "follows"; attach: ComponentId } /** * Defines the different kind of positioning a phantom can have */ -export type PhantomPositioning = FixedPhantomPositioning | FollowsPhantomPositioning - +export type PhantomPositioning = + | FixedPhantomPositioning + | FollowsPhantomPositioning /** * A player phantom is a kind of component that represents the future state of a player * according to the court's step information */ -export interface PlayerPhantom extends Component<"phantom", PhantomPositioning> { +export interface PlayerPhantom + extends Component<"phantom", PhantomPositioning> { readonly originPlayerId: ComponentId readonly ballState: BallState diff --git a/src/model/tactic/Tactic.ts b/src/model/tactic/Tactic.ts index b22473f..0ad312c 100644 --- a/src/model/tactic/Tactic.ts +++ b/src/model/tactic/Tactic.ts @@ -28,7 +28,7 @@ export interface Component { */ readonly id: ComponentId - readonly pos: Positioning, + readonly pos: Positioning readonly actions: Action[] } diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index cc51abb..83c4dac 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -403,7 +403,11 @@ function EditorView({ id: component.id, team: origin.team, role: origin.role, - pos: computePhantomPositioning(component, content, courtBounds()), + pos: computePhantomPositioning( + component, + content, + courtBounds(), + ), ballState: component.ballState, } } else { diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 2a6bb64..faba0d7 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -57,10 +57,10 @@ export default function HomePage() { } function Home({ - lastTactics, - allTactics, - teams, - }: { + lastTactics, + allTactics, + teams, +}: { lastTactics: Tactic[] allTactics: Tactic[] teams: Team[] @@ -77,10 +77,10 @@ function Home({ } function Body({ - lastTactics, - allTactics, - teams, - }: { + lastTactics, + allTactics, + teams, +}: { lastTactics: Tactic[] allTactics: Tactic[] teams: Team[] @@ -100,10 +100,10 @@ function Body({ } function SideMenu({ - width, - lastTactics, - teams, - }: { + width, + lastTactics, + teams, +}: { width: number lastTactics: Tactic[] teams: Team[] @@ -123,9 +123,9 @@ function SideMenu({ } function PersonalSpace({ - width, - allTactics, - }: { + width, + allTactics, +}: { width: number allTactics: Tactic[] }) { @@ -198,17 +198,15 @@ function TableData({ allTactics }: { allTactics: Tactic[] }) { function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) { return (
- { - allTactics.length == 0 - ?

Aucune tactique créée !

- : - - + {allTactics.length == 0 ? ( +

Aucune tactique créée !

+ ) : ( +
+ - -
- } - + + + )}
) }