import React, { CSSProperties, useEffect, useRef, useState } from "react" import "../style/editor.css" import TitleInput from "../components/TitleInput" import { BasketCourt } from "../components/editor/BasketCourt" import { Rack } from "../components/Rack" import { PlayerPiece } from "../components/editor/PlayerPiece" import { Player } from "../tactic/Player" import { Tactic, TacticContent } from "../tactic/Tactic" import { fetchAPI } from "../Fetcher" import { Team } from "../tactic/Team" import { calculateRatio } from "../Utils" import SavingState, { SaveStates } from "../components/editor/SavingState" const ERROR_STYLE: CSSProperties = { borderColor: "red", } export interface EditorViewProps { tactic: Tactic onContentChange: (tactic: TacticContent) => Promise onNameChange: (name: string) => Promise } /** * information about a player that is into a rack */ interface RackedPlayer { team: Team key: string } export default function Editor({ id, name, content, }: { id: number name: string content: string }) { return ( fetchAPI(`tactic/${id}/save`, { content }).then((r) => r.ok) } onNameChange={(name: string) => fetchAPI(`tactic/${id}/edit/name`, { name }).then((r) => r.ok) } /> ) } function EditorView({ tactic: { name, content }, onContentChange, onNameChange, }: EditorViewProps) { const [style, setStyle] = useState({}) const [saveState, setSaveState] = useState(SaveStates.Ok) const positions = ["1", "2", "3", "4", "5"] const [allies, setAllies] = useState( positions .filter( (role) => content.players.findIndex( (p) => p.team == Team.Allies && p.role == role, ) == -1, ) .map((key) => ({ team: Team.Allies, key })), ) const [opponents, setOpponents] = useState( positions .filter( (role) => content.players.findIndex( (p) => p.team == Team.Opponents && p.role == role, ) == -1, ) .map((key) => ({ team: Team.Opponents, key })), ) const [players, setPlayers] = useState(content.players) const courtDivContentRef = useRef(null) // The didMount ref is used to store a boolean flag in order to avoid calling 'onChange' when the editor is first rendered. const didMount = useRef(false) useEffect(() => { if (!didMount.current) { didMount.current = true return } setSaveState(SaveStates.Saving) onContentChange({ players }) .then((success) => { if (success) { setSaveState(SaveStates.Ok) } else { setSaveState(SaveStates.Err) } }) .catch(() => setSaveState(SaveStates.Err)) }, [players]) const canDetach = (ref: HTMLDivElement) => { const refBounds = ref.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect() // check if refBounds overlaps courtBounds return !( refBounds.top > courtBounds.bottom || refBounds.right < courtBounds.left || refBounds.bottom < courtBounds.top || refBounds.left > courtBounds.right ) } const onPieceDetach = (ref: HTMLDivElement, element: RackedPlayer) => { const refBounds = ref.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect() const { x, y } = calculateRatio(refBounds, courtBounds) setPlayers((players) => { return [ ...players, { id: players.length, team: element.team, role: element.key, rightRatio: x, bottomRatio: y, }, ] }) } return (
LEFT
{ onNameChange(new_name).then((success) => { if (success) { setStyle({}) } else { setStyle(ERROR_STYLE) } }) }} />
RIGHT
( )} /> ( )} />
{ setPlayers((players) => { const idx = players.findIndex( (p) => p.id === player.id, ) return players.toSpliced(idx, 1, player) }) }} onPlayerRemove={(player) => { setPlayers((players) => { const idx = players.findIndex( (p) => p.id === player.id, ) return players.toSpliced(idx, 1) }) switch (player.team) { case Team.Opponents: setOpponents((opponents) => [ ...opponents, { team: player.team, pos: player.role, key: player.role, }, ]) break case Team.Allies: setAllies((allies) => [ ...allies, { team: player.team, pos: player.role, key: player.role, }, ]) } }} />
) }