From 80c94d733f2ee1020c78e8500c05718423351eb7 Mon Sep 17 00:00:00 2001 From: maxime Date: Mon, 18 Mar 2024 22:29:14 +0100 Subject: [PATCH 1/4] add parent's content view in children --- src/components/arrows/BendableArrow.tsx | 6 +- src/components/editor/BasketCourt.tsx | 22 +++-- src/components/editor/CourtAction.tsx | 16 ++-- src/components/editor/StepsTree.tsx | 1 + src/editor/StepsDomain.ts | 4 +- src/editor/TacticContentDomains.ts | 24 ++++++ src/pages/Editor.tsx | 107 ++++++++++++++++-------- src/style/player.css | 5 ++ src/style/theme/default.css | 1 + 9 files changed, 137 insertions(+), 49 deletions(-) diff --git a/src/components/arrows/BendableArrow.tsx b/src/components/arrows/BendableArrow.tsx index 4b3615f..b730bbb 100644 --- a/src/components/arrows/BendableArrow.tsx +++ b/src/components/arrows/BendableArrow.tsx @@ -36,6 +36,7 @@ export interface BendableArrowProps { onSegmentsChanges: (edges: Segment[]) => void forceStraight: boolean wavy: boolean + readOnly: boolean startRadius?: number endRadius?: number @@ -87,6 +88,7 @@ function constraintInCircle(center: Pos, reference: Pos, radius: number): Pos { * @param segments * @param onSegmentsChanges * @param wavy + * @param readOnly * @param forceStraight * @param style * @param startRadius @@ -103,6 +105,7 @@ export default function BendableArrow({ forceStraight, wavy, + readOnly, style, startRadius = 0, @@ -531,7 +534,7 @@ export default function BendableArrow({ } fill="none" tabIndex={0} - onDoubleClick={addSegment} + onDoubleClick={readOnly ? undefined : addSegment} onKeyUp={(e) => { if (onDeleteRequested && e.key == "Delete") onDeleteRequested() @@ -555,6 +558,7 @@ export default function BendableArrow({ {!forceStraight && isSelected && + !readOnly && computePoints(area.current!.getBoundingClientRect())} ) diff --git a/src/components/editor/BasketCourt.tsx b/src/components/editor/BasketCourt.tsx index 68021f3..ba8c8c3 100644 --- a/src/components/editor/BasketCourt.tsx +++ b/src/components/editor/BasketCourt.tsx @@ -6,10 +6,11 @@ import { ComponentId, TacticComponent } from "../../model/tactic/Tactic" export interface BasketCourtProps { components: TacticComponent[] + parentComponents: TacticComponent[] | null previewAction: ActionPreview | null - renderComponent: (comp: TacticComponent) => ReactNode - renderActions: (comp: TacticComponent) => ReactNode[] + renderComponent: (comp: TacticComponent, isFromParent: boolean) => ReactNode + renderActions: (comp: TacticComponent, isFromParent: boolean) => ReactNode[] courtImage: ReactElement courtRef: RefObject @@ -22,6 +23,7 @@ export interface ActionPreview extends Action { export function BasketCourt({ components, + parentComponents, previewAction, renderComponent, @@ -37,15 +39,25 @@ export function BasketCourt({ style={{ position: "relative" }}> {courtImage} - {courtRef.current && components.map(renderComponent)} - {courtRef.current && components.flatMap(renderActions)} + {courtRef.current && + parentComponents && + parentComponents.map((i) => renderComponent(i, true))} + {courtRef.current && + parentComponents && + parentComponents.flatMap((i) => renderActions(i, true))} + + {courtRef.current && + components.map((i) => renderComponent(i, false))} + {courtRef.current && + components.flatMap((i) => renderActions(i, false))} {previewAction && ( {}} onActionChanges={() => {}} diff --git a/src/components/editor/CourtAction.tsx b/src/components/editor/CourtAction.tsx index 84f7fd5..54a6be2 100644 --- a/src/components/editor/CourtAction.tsx +++ b/src/components/editor/CourtAction.tsx @@ -7,22 +7,22 @@ import { ComponentId } from "../../model/tactic/Tactic" export interface CourtActionProps { origin: ComponentId action: Action - onActionChanges: (a: Action) => void - onActionDeleted: () => void + color: string courtRef: RefObject - isInvalid: boolean + isEditable: boolean + onActionChanges?: (a: Action) => void + onActionDeleted?: () => void } export function CourtAction({ origin, action, + color, onActionChanges, onActionDeleted, courtRef, - isInvalid, + isEditable, }: CourtActionProps) { - const color = isInvalid ? "red" : "black" - let head switch (action.type) { case ActionKind.DRIBBLE: @@ -49,9 +49,11 @@ export function CourtAction({ startPos={origin} segments={action.segments} onSegmentsChanges={(edges) => { - onActionChanges({ ...action, segments: edges }) + if (onActionChanges) + onActionChanges({ ...action, segments: edges }) }} wavy={action.type == ActionKind.DRIBBLE} + readOnly={!isEditable} //TODO place those magic values in constants endRadius={action.target ? 26 : 17} startRadius={10} diff --git a/src/components/editor/StepsTree.tsx b/src/components/editor/StepsTree.tsx index 952b268..dde6534 100644 --- a/src/components/editor/StepsTree.tsx +++ b/src/components/editor/StepsTree.tsx @@ -70,6 +70,7 @@ function StepsTreeNode({ onSegmentsChanges={() => {}} forceStraight={true} wavy={false} + readOnly={true} //TODO remove magic constants startRadius={10} endRadius={10} diff --git a/src/editor/StepsDomain.ts b/src/editor/StepsDomain.ts index e788a9c..a2b739a 100644 --- a/src/editor/StepsDomain.ts +++ b/src/editor/StepsDomain.ts @@ -98,9 +98,9 @@ export function getAvailableId(root: StepInfoNode): number { export function getParent( root: StepInfoNode, - node: StepInfoNode, + node: number, ): StepInfoNode | null { - if (root.children.find((n) => n.id === node.id)) return root + if (root.children.find((n) => n.id === node)) return root for (const child of root.children) { const result = getParent(child, node) diff --git a/src/editor/TacticContentDomains.ts b/src/editor/TacticContentDomains.ts index 319e4a0..da38860 100644 --- a/src/editor/TacticContentDomains.ts +++ b/src/editor/TacticContentDomains.ts @@ -472,3 +472,27 @@ export function drainTerminalStateOnChildContent( return gotUpdated ? childContent : null } + +export function mapToParentContent(content: StepContent): StepContent { + return { + ...content, + components: content.components.map((p) => { + if (p.type == "ball") return p + return { + ...p, + id: p.id + "-parent", + actions: p.actions.map((a) => ({ + ...a, + target: a.target + "-parent", + segments: a.segments.map((s) => ({ + ...s, + next: + typeof s.next === "string" + ? s.next + "-parent" + : s.next, + })), + })), + } + }), + } +} diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index 03e8121..3066340 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -42,6 +42,7 @@ import { dropBallOnComponent, getComponentCollided, getRackPlayers, + mapToParentContent, moveComponent, placeBallAt, placeObjectAt, @@ -202,6 +203,8 @@ function EditorPageWrapper({ service }: { service: TacticService }) { [stepsVersions, service, stepId, stepsTree], ) + const [parentContent, setParentContent] = useState(null) + const [stepContent, setStepContent, saveState] = useContentState( { components: [] }, @@ -271,21 +274,30 @@ function EditorPageWrapper({ service }: { service: TacticService }) { if (isNotInit) init() }, [isNotInit, service, setStepContent, stepsVersions]) - const editorService: EditorService = useMemo( - () => ({ + const editorService: EditorService = useMemo(() => { + let internalStepsTree = stepsTree + return { async addStep( parent: StepInfoNode, content: StepContent, ): Promise { const result = await service.addStep(parent, content) - if (typeof result !== "string") - setStepsTree(addStepNode(stepsTree!, parent, result)) + if (typeof result !== "string") { + internalStepsTree = addStepNode( + internalStepsTree!, + parent, + result, + ) + setStepsTree(internalStepsTree) + } return result }, async removeStep(step: number): Promise { const result = await service.removeStep(step) - if (typeof result !== "string") - setStepsTree(removeStepNode(stepsTree!, step)) + if (typeof result !== "string") { + internalStepsTree = removeStepNode(internalStepsTree!, step) + setStepsTree(internalStepsTree) + } stepsVersions.delete(step) return result }, @@ -304,12 +316,19 @@ function EditorPageWrapper({ service }: { service: TacticService }) { async selectStep(step: number): Promise { const result = await service.getContent(step) if (typeof result === "string") return result + const stepParent = getParent(internalStepsTree!, step)?.id + if (stepParent) { + const parentResult = await service.getContent(stepParent) + if (typeof parentResult === "string") return parentResult + setParentContent(mapToParentContent(parentResult)) + } else { + setParentContent(null) + } setStepId(step) setStepContent(result, false) }, - }), - [stepsVersions, service, setStepContent, stepsTree], - ) + } + }, [stepsVersions, service, setStepContent, stepsTree]) if (panicMessage) { return

{panicMessage}

@@ -326,6 +345,7 @@ function EditorPageWrapper({ service }: { service: TacticService }) { stepId={stepId} stepsTree={stepsTree} contentSaveState={saveState} + parentContent={parentContent} content={stepContent} service={editorService} courtRef={courtRef} @@ -337,10 +357,12 @@ export interface EditorViewProps { stepsTree: StepInfoNode name: string courtType: CourtType - content: StepContent contentSaveState: SaveState stepId: number + parentContent: StepContent | null + content: StepContent + courtRef: RefObject service: EditorService @@ -349,6 +371,7 @@ export interface EditorViewProps { function EditorPage({ name, courtType, + parentContent, content, stepId, contentSaveState, @@ -516,9 +539,12 @@ function EditorPage({ ) const renderPlayer = useCallback( - (component: PlayerLike) => { + (component: PlayerLike, isFromParent: boolean) => { let info: PlayerInfo const isPhantom = component.type == "phantom" + + let forceFreeze = isFromParent + if (isPhantom) { const origin = getOrigin(component, content.components) info = { @@ -536,24 +562,31 @@ function EditorPage({ } else { info = component - if (component.frozen) { - return ( - - renderAvailablePlayerActions(info, component) - } - /> - ) - } + forceFreeze ||= component.frozen + } + + const className = + (isPhantom ? "phantom" : "player") + + " " + + (isFromParent ? "from-parent" : "") + + if (forceFreeze) { + return ( + + renderAvailablePlayerActions(info, component) + } + /> + ) } return ( validatePlayerPosition(component, info, newPos) @@ -604,11 +637,11 @@ function EditorPage({ ) const renderComponent = useCallback( - (component: TacticComponent) => { + (component: TacticComponent, isFromParent: boolean) => { if (component.type === "player" || component.type === "phantom") { - return renderPlayer(component) + return renderPlayer(component, isFromParent) } - if (component.type === BALL_TYPE) { + if (component.type === BALL_TYPE && !isFromParent) { return ( + (component: TacticComponent, isFromParent: boolean) => component.actions.map((action, i) => { return ( { - doDeleteAction(action, i, component) + if (!isFromParent) + doDeleteAction(action, i, component) + }} + onActionChanges={(action) => { + if (!isFromParent) + doUpdateAction(component, action, i) }} - onActionChanges={(action) => - doUpdateAction(component, action, i) - } /> ) }), @@ -698,6 +734,7 @@ function EditorPage({
} courtRef={courtRef} @@ -731,7 +768,9 @@ function EditorPage({ onRemoveNode={useCallback( async (removed) => { await service.removeStep(removed.id) - await service.selectStep(getParent(stepsTree, removed)!.id) + await service.selectStep( + getParent(stepsTree, removed.id)!.id, + ) }, [service, stepsTree], )} diff --git a/src/style/player.css b/src/style/player.css index b03123b..80dd4e5 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -6,6 +6,11 @@ opacity: 50%; } +.from-parent .player-piece { + color: white; + background-color: var(--player-from-parent-color); +} + .player-content { display: flex; flex-direction: column; diff --git a/src/style/theme/default.css b/src/style/theme/default.css index 19702b0..05ed963 100644 --- a/src/style/theme/default.css +++ b/src/style/theme/default.css @@ -9,6 +9,7 @@ --selected-team-secondarycolor: #000000; --player-allies-color: #64e4f5; --player-opponents-color: #f59264; + --player-from-parent-color: #494949; --buttons-shadow-color: #a8a8a8; -- 2.36.3 From 69edb10b0446bf52a7335fb122a56c98436cba2c Mon Sep 17 00:00:00 2001 From: maxime Date: Mon, 18 Mar 2024 22:44:07 +0100 Subject: [PATCH 2/4] fix player with or without ball to the same size --- src/components/editor/PlayerPiece.tsx | 4 +++- src/style/player.css | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/editor/PlayerPiece.tsx b/src/components/editor/PlayerPiece.tsx index 1b52ff8..1a7067b 100644 --- a/src/components/editor/PlayerPiece.tsx +++ b/src/components/editor/PlayerPiece.tsx @@ -10,7 +10,9 @@ export interface PlayerPieceProps { export function PlayerPiece({ team, text, hasBall }: PlayerPieceProps) { let className = `player-piece ${team}` if (hasBall) { - className += ` player-piece-has-ball` + className += " player-piece-has-ball" + } else { + className += " player-piece-has-no-ball" } return ( diff --git a/src/style/player.css b/src/style/player.css index 80dd4e5..4e2e368 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -45,6 +45,10 @@ border-color: var(--player-piece-ball-border-color); } +.player-piece-has-no-ball { + padding: 2px +} + .player-actions { display: flex; -- 2.36.3 From 882e45f3288459053e0f030dc0d7e059912d3ae8 Mon Sep 17 00:00:00 2001 From: maxime Date: Thu, 21 Mar 2024 22:28:21 +0100 Subject: [PATCH 3/4] fix steps bugs --- src/editor/PlayerDomains.ts | 8 +- src/editor/TacticContentDomains.ts | 129 ++++++++++++++--------------- src/pages/Editor.tsx | 10 ++- src/style/player.css | 1 + src/style/template/header.css | 3 + 5 files changed, 78 insertions(+), 73 deletions(-) diff --git a/src/editor/PlayerDomains.ts b/src/editor/PlayerDomains.ts index 1b3c918..e361f93 100644 --- a/src/editor/PlayerDomains.ts +++ b/src/editor/PlayerDomains.ts @@ -279,9 +279,11 @@ export function removePlayer( if (action.type !== ActionKind.SHOOT) { continue } - const actionTarget = content.components.find( - (c) => c.id === action.target, - )! as PlayerLike + if (typeof action.target !== "string") + continue + const actionTarget = tryGetComponent(action.target, content.components) + if (actionTarget === undefined) + continue //the target was maybe removed return ( spreadNewStateFromOriginStateChange( actionTarget, diff --git a/src/editor/TacticContentDomains.ts b/src/editor/TacticContentDomains.ts index da38860..e328737 100644 --- a/src/editor/TacticContentDomains.ts +++ b/src/editor/TacticContentDomains.ts @@ -1,34 +1,13 @@ import { equals, Pos, ratioWithinBase } from "../geo/Pos" -import { - BallState, - Player, - PlayerInfo, - PlayerLike, - PlayerPhantom, - PlayerTeam, -} from "../model/tactic/Player" -import { - Ball, - BALL_ID, - BALL_TYPE, - CourtObject, -} from "../model/tactic/CourtObjects" -import { - ComponentId, - StepContent, - TacticComponent, -} from "../model/tactic/Tactic" +import { BallState, Player, PlayerInfo, PlayerLike, PlayerPhantom, PlayerTeam } from "../model/tactic/Player" +import { Ball, BALL_ID, BALL_TYPE, CourtObject } from "../model/tactic/CourtObjects" +import { ComponentId, StepContent, TacticComponent } from "../model/tactic/Tactic" import { overlaps } from "../geo/Box" import { RackedCourtObject, RackedPlayer } from "./RackedItems" -import { - getComponent, - getOrigin, - getPrecomputedPosition, - tryGetComponent, -} from "./PlayerDomains" -import { ActionKind } from "../model/tactic/Action.ts" +import { getComponent, getOrigin, getPrecomputedPosition, removePlayer, tryGetComponent } from "./PlayerDomains" +import { Action, ActionKind } from "../model/tactic/Action.ts" import { spreadNewStateFromOriginStateChange } from "./ActionsDomains.ts" export function placePlayerAt( @@ -200,9 +179,9 @@ export function moveComponent( phantomIdx == 0 ? origin : getComponent( - originPathItems[phantomIdx - 1], - content.components, - ) + 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( { @@ -210,18 +189,18 @@ export function moveComponent( 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, + 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, ), }, @@ -234,9 +213,9 @@ export function moveComponent( ...component, pos: isPhantom ? { - type: "fixed", - ...newPos, - } + type: "fixed", + ...newPos, + } : newPos, }, content, @@ -321,9 +300,9 @@ export function computeTerminalState( comp.type === "player" ? getPlayerTerminalState(comp, content, computedPositions) : { - ...comp, - frozen: true, - }, + ...comp, + frozen: true, + }, ) return { @@ -458,14 +437,12 @@ export function drainTerminalStateOnChildContent( const initialChildCompsCount = childContent.components.length - //filter out all frozen components that are not present on the parent's terminal state anymore - childContent = { - components: childContent.components.filter( - (comp) => - comp.type === "phantom" || - !comp.frozen || - tryGetComponent(comp.id, parentTerminalState.components), - ), + for (const component of childContent.components) { + if (component.type !== "phantom" && component.frozen && !tryGetComponent(component.id, parentTerminalState.components)) { + if (component.type === "player") + childContent = removePlayer(component, childContent) + else childContent = {...childContent, components: childContent.components.filter(c => c.id !== component.id)} + } } gotUpdated ||= childContent.components.length !== initialChildCompsCount @@ -474,25 +451,45 @@ export function drainTerminalStateOnChildContent( } export function mapToParentContent(content: StepContent): StepContent { + + function mapToParentActions(actions: Action[]): Action[] { + return actions.map((a) => ({ + ...a, + target: a.target + "-parent", + segments: a.segments.map((s) => ({ + ...s, + next: + typeof s.next === "string" + ? s.next + "-parent" + : s.next, + })), + })) + } + return { ...content, components: content.components.map((p) => { if (p.type == "ball") return p + if (p.type == "player") { + return { + ...p, + id: p.id + "-parent", + actions: mapToParentActions(p.actions), + path: p.path && { items: p.path.items.map(p => p + "-parent") }, + } + } return { ...p, + pos: p.pos.type == "follows" ? { ...p.pos, attach: p.pos.attach + "-parent" } : p.pos, id: p.id + "-parent", - actions: p.actions.map((a) => ({ - ...a, - target: a.target + "-parent", - segments: a.segments.map((s) => ({ - ...s, - next: - typeof s.next === "string" - ? s.next + "-parent" - : s.next, - })), - })), + originPlayerId: p.originPlayerId + "-parent", + actions: mapToParentActions(p.actions), } }), } } + + +export function selectContent(id: string, content: StepContent, parentContent: StepContent | null): StepContent { + return parentContent && id.endsWith("-parent") ? parentContent : content +} \ No newline at end of file diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index 3066340..c978661 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -47,7 +47,7 @@ import { placeBallAt, placeObjectAt, placePlayerAt, - removeBall, + removeBall, selectContent, updateComponent, } from "../editor/TacticContentDomains" @@ -488,7 +488,7 @@ function EditorPage({ ) == -1 isFrozen = player.frozen } else { - const origin = getOrigin(player, content.components) + const origin = getOrigin(player, selectContent(player.id, content, parentContent).components) const path = origin.path! // phantoms can only place other arrows if they are the head of the path canPlaceArrows = @@ -545,15 +545,17 @@ function EditorPage({ let forceFreeze = isFromParent + const usedContent = isFromParent ? parentContent! : content + if (isPhantom) { - const origin = getOrigin(component, content.components) + const origin = getOrigin(component, usedContent.components) info = { id: component.id, team: origin.team, role: origin.role, pos: computePhantomPositioning( component, - content, + usedContent, relativePositions, courtBounds(), ), diff --git a/src/style/player.css b/src/style/player.css index 4e2e368..1ddc08c 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -51,6 +51,7 @@ .player-actions { display: flex; + pointer-events: none; position: absolute; flex-direction: row; diff --git a/src/style/template/header.css b/src/style/template/header.css index 07db237..dba7305 100644 --- a/src/style/template/header.css +++ b/src/style/template/header.css @@ -15,6 +15,7 @@ #img-account { cursor: pointer; + margin-right: 5px; } #header-left, @@ -28,6 +29,8 @@ flex-direction: column; justify-content: center; align-items: end; + color: white; + margin-right: 5px; } #clickable-header-right:hover #username { -- 2.36.3 From bef196e09e4c053757ead3e6d9e917c5acf59fb5 Mon Sep 17 00:00:00 2001 From: maxime Date: Fri, 22 Mar 2024 20:47:00 +0100 Subject: [PATCH 4/4] Apply suggestions and format --- src/components/editor/BasketCourt.tsx | 6 +- src/editor/PlayerDomains.ts | 11 +-- src/editor/TacticContentDomains.ts | 110 +++++++++++++++++--------- src/pages/Editor.tsx | 8 +- src/style/player.css | 2 +- 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/src/components/editor/BasketCourt.tsx b/src/components/editor/BasketCourt.tsx index ba8c8c3..e3cddd2 100644 --- a/src/components/editor/BasketCourt.tsx +++ b/src/components/editor/BasketCourt.tsx @@ -40,11 +40,9 @@ export function BasketCourt({ {courtImage} {courtRef.current && - parentComponents && - parentComponents.map((i) => renderComponent(i, true))} + parentComponents?.map((i) => renderComponent(i, true))} {courtRef.current && - parentComponents && - parentComponents.flatMap((i) => renderActions(i, true))} + parentComponents?.flatMap((i) => renderActions(i, true))} {courtRef.current && components.map((i) => renderComponent(i, false))} diff --git a/src/editor/PlayerDomains.ts b/src/editor/PlayerDomains.ts index e361f93..2a7e28d 100644 --- a/src/editor/PlayerDomains.ts +++ b/src/editor/PlayerDomains.ts @@ -279,11 +279,12 @@ export function removePlayer( if (action.type !== ActionKind.SHOOT) { continue } - if (typeof action.target !== "string") - continue - const actionTarget = tryGetComponent(action.target, content.components) - if (actionTarget === undefined) - continue //the target was maybe removed + if (typeof action.target !== "string") continue + const actionTarget = tryGetComponent( + action.target, + content.components, + ) + if (actionTarget === undefined) continue //the target was maybe removed return ( spreadNewStateFromOriginStateChange( actionTarget, diff --git a/src/editor/TacticContentDomains.ts b/src/editor/TacticContentDomains.ts index e328737..c7b2965 100644 --- a/src/editor/TacticContentDomains.ts +++ b/src/editor/TacticContentDomains.ts @@ -1,12 +1,34 @@ import { equals, Pos, ratioWithinBase } from "../geo/Pos" -import { BallState, Player, PlayerInfo, PlayerLike, PlayerPhantom, PlayerTeam } from "../model/tactic/Player" -import { Ball, BALL_ID, BALL_TYPE, CourtObject } from "../model/tactic/CourtObjects" -import { ComponentId, StepContent, TacticComponent } from "../model/tactic/Tactic" +import { + BallState, + Player, + PlayerInfo, + PlayerLike, + PlayerPhantom, + PlayerTeam, +} from "../model/tactic/Player" +import { + Ball, + BALL_ID, + BALL_TYPE, + CourtObject, +} from "../model/tactic/CourtObjects" +import { + ComponentId, + StepContent, + TacticComponent, +} from "../model/tactic/Tactic" import { overlaps } from "../geo/Box" import { RackedCourtObject, RackedPlayer } from "./RackedItems" -import { getComponent, getOrigin, getPrecomputedPosition, removePlayer, tryGetComponent } from "./PlayerDomains" +import { + getComponent, + getOrigin, + getPrecomputedPosition, + removePlayer, + tryGetComponent, +} from "./PlayerDomains" import { Action, ActionKind } from "../model/tactic/Action.ts" import { spreadNewStateFromOriginStateChange } from "./ActionsDomains.ts" @@ -179,9 +201,9 @@ export function moveComponent( phantomIdx == 0 ? origin : getComponent( - originPathItems[phantomIdx - 1], - content.components, - ) + 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( { @@ -189,18 +211,18 @@ export function moveComponent( 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, + 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, ), }, @@ -213,9 +235,9 @@ export function moveComponent( ...component, pos: isPhantom ? { - type: "fixed", - ...newPos, - } + type: "fixed", + ...newPos, + } : newPos, }, content, @@ -300,9 +322,9 @@ export function computeTerminalState( comp.type === "player" ? getPlayerTerminalState(comp, content, computedPositions) : { - ...comp, - frozen: true, - }, + ...comp, + frozen: true, + }, ) return { @@ -438,10 +460,20 @@ export function drainTerminalStateOnChildContent( const initialChildCompsCount = childContent.components.length for (const component of childContent.components) { - if (component.type !== "phantom" && component.frozen && !tryGetComponent(component.id, parentTerminalState.components)) { + if ( + component.type !== "phantom" && + component.frozen && + !tryGetComponent(component.id, parentTerminalState.components) + ) { if (component.type === "player") childContent = removePlayer(component, childContent) - else childContent = {...childContent, components: childContent.components.filter(c => c.id !== component.id)} + else + childContent = { + ...childContent, + components: childContent.components.filter( + (c) => c.id !== component.id, + ), + } } } @@ -451,17 +483,13 @@ export function drainTerminalStateOnChildContent( } export function mapToParentContent(content: StepContent): StepContent { - function mapToParentActions(actions: Action[]): Action[] { return actions.map((a) => ({ ...a, target: a.target + "-parent", segments: a.segments.map((s) => ({ ...s, - next: - typeof s.next === "string" - ? s.next + "-parent" - : s.next, + next: typeof s.next === "string" ? s.next + "-parent" : s.next, })), })) } @@ -475,12 +503,17 @@ export function mapToParentContent(content: StepContent): StepContent { ...p, id: p.id + "-parent", actions: mapToParentActions(p.actions), - path: p.path && { items: p.path.items.map(p => p + "-parent") }, + path: p.path && { + items: p.path.items.map((p) => p + "-parent"), + }, } } return { ...p, - pos: p.pos.type == "follows" ? { ...p.pos, attach: p.pos.attach + "-parent" } : p.pos, + pos: + p.pos.type == "follows" + ? { ...p.pos, attach: p.pos.attach + "-parent" } + : p.pos, id: p.id + "-parent", originPlayerId: p.originPlayerId + "-parent", actions: mapToParentActions(p.actions), @@ -489,7 +522,10 @@ export function mapToParentContent(content: StepContent): StepContent { } } - -export function selectContent(id: string, content: StepContent, parentContent: StepContent | null): StepContent { +export function selectContent( + id: string, + content: StepContent, + parentContent: StepContent | null, +): StepContent { return parentContent && id.endsWith("-parent") ? parentContent : content -} \ No newline at end of file +} diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index c978661..a9ece7d 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -47,7 +47,8 @@ import { placeBallAt, placeObjectAt, placePlayerAt, - removeBall, selectContent, + removeBall, + selectContent, updateComponent, } from "../editor/TacticContentDomains" @@ -488,7 +489,10 @@ function EditorPage({ ) == -1 isFrozen = player.frozen } else { - const origin = getOrigin(player, selectContent(player.id, content, parentContent).components) + const origin = getOrigin( + player, + selectContent(player.id, content, parentContent).components, + ) const path = origin.path! // phantoms can only place other arrows if they are the head of the path canPlaceArrows = diff --git a/src/style/player.css b/src/style/player.css index 1ddc08c..8817474 100644 --- a/src/style/player.css +++ b/src/style/player.css @@ -46,7 +46,7 @@ } .player-piece-has-no-ball { - padding: 2px + padding: 2px; } .player-actions { -- 2.36.3