fix css + verify and format

pull/77/head
Vivien DUFOUR 1 year ago
parent c68344dab0
commit d858cc3113

@ -1,7 +1,7 @@
import "../../style/ball.css" import "../../style/ball.css"
import BallSvg from "../../assets/icon/ball.svg?react" import BallSvg from "../../assets/icon/ball.svg?react"
import {Ball} from "../../tactic/CourtObjects"; import { Ball } from "../../tactic/CourtObjects"
export interface CourtBallProps { export interface CourtBallProps {
onMoved: (rect: DOMRect) => void onMoved: (rect: DOMRect) => void
@ -10,8 +10,5 @@ export interface CourtBallProps {
} }
export function BallPiece() { export function BallPiece() {
return ( return <BallSvg className={"ball"} />
<BallSvg className={"ball"}/>
)
} }

@ -1,10 +1,10 @@
import "../../style/basket_court.css" import "../../style/basket_court.css"
import {RefObject} from "react" import { RefObject } from "react"
import CourtPlayer from "./CourtPlayer" import CourtPlayer from "./CourtPlayer"
import {Player} from "../../tactic/Player" import { Player } from "../../tactic/Player"
import {CourtObject} from "../../tactic/CourtObjects"; import { CourtObject } from "../../tactic/CourtObjects"
import {CourtBall} from "./CourtBall"; import { CourtBall } from "./CourtBall"
export interface BasketCourtProps { export interface BasketCourtProps {
players: Player[] players: Player[]
@ -13,9 +13,9 @@ export interface BasketCourtProps {
onPlayerRemove: (p: Player) => void onPlayerRemove: (p: Player) => void
onPlayerChange: (p: Player) => void onPlayerChange: (p: Player) => void
onBallRemove : () => void onBallRemove: () => void
onBallMoved: (ball: DOMRect) => void, onBallMoved: (ball: DOMRect) => void
courtImage: string courtImage: string
courtRef: RefObject<HTMLDivElement> courtRef: RefObject<HTMLDivElement>
@ -30,14 +30,13 @@ export function BasketCourt({
onPlayerChange, onPlayerChange,
courtImage, courtImage,
courtRef, courtRef,
}: BasketCourtProps) { }: BasketCourtProps) {
return ( return (
<div <div
id="court-container" id="court-container"
ref={courtRef} ref={courtRef}
style={{position: "relative"}}> style={{ position: "relative" }}>
<img src={courtImage} alt={"court"} id="court-svg"/> <img src={courtImage} alt={"court"} id="court-svg" />
{players.map((player) => { {players.map((player) => {
return ( return (
<CourtPlayer <CourtPlayer
@ -51,19 +50,19 @@ export function BasketCourt({
) )
})} })}
{objects.map(object => { {objects.map((object) => {
if (object.type == "ball") { if (object.type == "ball") {
return <CourtBall return (
<CourtBall
onMoved={onBallMoved} onMoved={onBallMoved}
ball={object} ball={object}
onRemove={onBallRemove} onRemove={onBallRemove}
key="ball" key="ball"
/> />
)
} }
throw new Error("unknown court object", object.type) throw new Error("unknown court object", object.type)
})} })}
</div> </div>
) )
} }

@ -1,9 +1,8 @@
import React, {useRef} from "react"; import React, { useRef } from "react"
import Draggable from "react-draggable"; import Draggable from "react-draggable"
import {BallPiece, CourtBallProps} from "./BallPiece"; import { BallPiece, CourtBallProps } from "./BallPiece"
export function CourtBall({ onMoved, ball, onRemove }: CourtBallProps) {
export function CourtBall({onMoved, ball, onRemove}: CourtBallProps) {
const pieceRef = useRef<HTMLDivElement>(null) const pieceRef = useRef<HTMLDivElement>(null)
const x = ball.rightRatio const x = ball.rightRatio
@ -12,9 +11,9 @@ export function CourtBall({onMoved, ball, onRemove}: CourtBallProps) {
return ( return (
<Draggable <Draggable
onStop={() => onMoved(pieceRef.current!.getBoundingClientRect())} onStop={() => onMoved(pieceRef.current!.getBoundingClientRect())}
nodeRef={pieceRef} nodeRef={pieceRef}>
> <div
<div className={"ball-div"} className={"ball-div"}
ref={pieceRef} ref={pieceRef}
tabIndex={0} tabIndex={0}
onKeyUp={(e) => { onKeyUp={(e) => {
@ -24,9 +23,8 @@ export function CourtBall({onMoved, ball, onRemove}: CourtBallProps) {
position: "absolute", position: "absolute",
left: `${x * 100}%`, left: `${x * 100}%`,
top: `${y * 100}%`, top: `${y * 100}%`,
}} }}>
> <BallPiece />
<BallPiece/>
</div> </div>
</Draggable> </Draggable>
) )

@ -1,10 +1,10 @@
import {RefObject, useRef} from "react" import { RefObject, useRef } from "react"
import "../../style/player.css" import "../../style/player.css"
import {BallPiece} from "./BallPiece" import { BallPiece } from "./BallPiece"
import Draggable from "react-draggable" import Draggable from "react-draggable"
import {PlayerPiece} from "./PlayerPiece" import { PlayerPiece } from "./PlayerPiece"
import {Player} from "../../tactic/Player" import { Player } from "../../tactic/Player"
import {calculateRatio} from "../../Utils" import { calculateRatio } from "../../Utils"
export interface PlayerProps { export interface PlayerProps {
player: Player player: Player
@ -68,13 +68,20 @@ export default function CourtPlayer({
if (e.key == "Delete") onRemove() if (e.key == "Delete") onRemove()
}}> }}>
<div className="player-selection-tab"> <div className="player-selection-tab">
{hasBall && (<Draggable nodeRef={ballPiece} {hasBall && (
onStop={() => onBallDrop(ballPiece.current!.getBoundingClientRect())} <Draggable
position={{x:0, y: 0}}> nodeRef={ballPiece}
onStop={() =>
onBallDrop(
ballPiece.current!.getBoundingClientRect(),
)
}
position={{ x: 0, y: 0 }}>
<div ref={ballPiece}> <div ref={ballPiece}>
<BallPiece /> <BallPiece />
</div> </div>
</Draggable>)} </Draggable>
)}
</div> </div>
<PlayerPiece <PlayerPiece
team={player.team} team={player.team}

@ -8,9 +8,7 @@
width: 20px; width: 20px;
height: 20px; height: 20px;
cursor: pointer; cursor: pointer;
tabIndex: 0;
} }
.ball-div:focus-within { .ball-div:focus-within {
} }

@ -32,6 +32,8 @@
#racks { #racks {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
height: 25px;
} }
.title-input { .title-input {
@ -46,6 +48,10 @@
#allies-rack { #allies-rack {
width: 125px; width: 125px;
min-width: 125px; min-width: 125px;
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: flex-start;
} }
#opponent-rack { #opponent-rack {
@ -111,5 +117,3 @@
.save-state-guest { .save-state-guest {
color: gray; color: gray;
} }

@ -1,13 +1,10 @@
export type CourtObject = { type: "ball" } & Ball
export type CourtObject = {type: "ball"} & Ball
export interface Ball { export interface Ball {
/** /**
* The ball is a "ball" court object * The ball is a "ball" court object
*/ */
readonly type: "ball", readonly type: "ball"
/** /**
* Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle) * Percentage of the player's position to the bottom (0 means top, 1 means bottom, 0.5 means middle)

@ -1,5 +1,5 @@
import { Player } from "./Player" import { Player } from "./Player"
import {CourtObject} from "./CourtObjects"; import { CourtObject } from "./CourtObjects"
export interface Tactic { export interface Tactic {
id: number id: number

@ -1,4 +1,11 @@
import {CSSProperties, Dispatch, SetStateAction, useCallback, useRef, useState,} from "react" import {
CSSProperties,
Dispatch,
SetStateAction,
useCallback,
useRef,
useState,
} from "react"
import "../style/editor.css" import "../style/editor.css"
import TitleInput from "../components/TitleInput" import TitleInput from "../components/TitleInput"
import { BasketCourt } from "../components/editor/BasketCourt" import { BasketCourt } from "../components/editor/BasketCourt"
@ -7,23 +14,23 @@ import plainCourt from "../assets/court/full_court.svg"
import halfCourt from "../assets/court/half_court.svg" import halfCourt from "../assets/court/half_court.svg"
import { BallPiece } from "../components/editor/BallPiece"
import { Rack } from "../components/Rack" import { Rack } from "../components/Rack"
import { PlayerPiece } from "../components/editor/PlayerPiece" import { PlayerPiece } from "../components/editor/PlayerPiece"
import { Player } from "../tactic/Player"
import {BallPiece, CourtBall} from "../components/editor/BallPiece";
import { Tactic, TacticContent } from "../tactic/Tactic" import { Tactic, TacticContent } from "../tactic/Tactic"
import { fetchAPI } from "../Fetcher" import { fetchAPI } from "../Fetcher"
import { Team } from "../tactic/Team" import { Team } from "../tactic/Team"
import { calculateRatio } from "../Utils" import { calculateRatio } from "../Utils"
import SavingState, { import SavingState, {
SaveState, SaveState,
SaveStates, SaveStates,
} from "../components/editor/SavingState" } from "../components/editor/SavingState"
import {CourtObject} from "../tactic/CourtObjects"; import {CourtObject} from "../tactic/CourtObjects";
import {Simulate} from "react-dom/test-utils";
const ERROR_STYLE: CSSProperties = { const ERROR_STYLE: CSSProperties = {
@ -57,12 +64,14 @@ interface RackedPlayer {
type RackedCourtObject = { key: "ball" } type RackedCourtObject = { key: "ball" }
export default function Editor({ export default function Editor({
id, id,
name, name,
courtType, courtType,
content, content,
}: EditorProps) { }: EditorProps) {
const isInGuestMode = id == -1 const isInGuestMode = id == -1
const storage_content = localStorage.getItem(GUEST_MODE_CONTENT_STORAGE_KEY) const storage_content = localStorage.getItem(GUEST_MODE_CONTENT_STORAGE_KEY)
@ -101,17 +110,17 @@ export default function Editor({
(r) => r.ok, (r) => r.ok,
) )
}} }}
courtType={courtType}/> courtType={courtType}
/>
) )
} }
function EditorView({ function EditorView({
tactic: {id, name, content: initialContent}, tactic: { id, name, content: initialContent },
onContentChange, onContentChange,
onNameChange, onNameChange,
courtType, courtType,
}: EditorViewProps) { }: EditorViewProps) {
const isInGuestMode = id == -1 const isInGuestMode = id == -1
const [titleStyle, setTitleStyle] = useState<CSSProperties>({}) const [titleStyle, setTitleStyle] = useState<CSSProperties>({})
@ -128,12 +137,14 @@ function EditorView({
getRackPlayers(Team.Opponents, content.players), getRackPlayers(Team.Opponents, content.players),
) )
const [objects, setObjects] = useState<RackedCourtObject[]>(isBallOnCourt(content) ? [] : [{key: "ball"}]) const [objects, setObjects] = useState<RackedCourtObject[]>(
isBallOnCourt(content) ? [] : [{ key: "ball" }],
)
const courtDivContentRef = useRef<HTMLDivElement>(null) const courtDivContentRef = useRef<HTMLDivElement>(null)
const canDetach = (bounds: DOMRect) => { const isBoundsOnCourt = (bounds: DOMRect) => {
const courtBounds = courtDivContentRef.current!.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect()
// check if refBounds overlaps courtBounds // check if refBounds overlaps courtBounds
@ -145,8 +156,6 @@ function EditorView({
) )
} }
const onPieceDetach = (ref: HTMLDivElement, element: RackedPlayer) => { const onPieceDetach = (ref: HTMLDivElement, element: RackedPlayer) => {
const refBounds = ref.getBoundingClientRect() const refBounds = ref.getBoundingClientRect()
const courtBounds = courtDivContentRef.current!.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect()
@ -171,33 +180,40 @@ function EditorView({
}) })
} }
const onObjectDetach = (ref: HTMLDivElement, rackedObject: RackedCourtObject) => { const onObjectDetach = (
ref: HTMLDivElement,
rackedObject: RackedCourtObject,
) => {
const refBounds = ref.getBoundingClientRect() const refBounds = ref.getBoundingClientRect()
const courtBounds = courtDivContentRef.current!.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect()
const {x, y} = calculateRatio(refBounds, courtBounds) const { x, y } = calculateRatio(refBounds, courtBounds)
let courtObject: CourtObject let courtObject: CourtObject
switch (rackedObject.key) { switch (rackedObject.key) {
case "ball": case "ball":
const ballObj = content.objects.findIndex(o => o.type == "ball") const ballObj = content.objects.findIndex(
const playerCollidedIdx = getPlayerCollided(refBounds, content.players) (o) => o.type == "ball",
if(playerCollidedIdx != -1) { )
const playerCollidedIdx = getPlayerCollided(
refBounds,
content.players,
)
if (playerCollidedIdx != -1) {
onBallDropOnPlayer(playerCollidedIdx) onBallDropOnPlayer(playerCollidedIdx)
setContent((content) => { setContent((content) => {
return{ return {
...content, ...content,
objects : content.objects.toSpliced(ballObj, 1) objects: content.objects.toSpliced(ballObj, 1),
} }
}) })
return return
} } else {
else {
courtObject = { courtObject = {
type: "ball", type: "ball",
rightRatio: x, rightRatio: x,
bottomRatio: y bottomRatio: y,
} }
} }
break break
@ -207,81 +223,100 @@ function EditorView({
} }
setContent((content) => { setContent((content) => {
return ({ return {
...content, ...content,
objects: [ objects: [...content.objects, courtObject],
...content.objects, }
courtObject,
]
})
}) })
} }
const getPlayerCollided = (bounds: DOMRect, players: Player[]): number | -1 => { const getPlayerCollided = (
bounds: DOMRect,
players: Player[],
): number | -1 => {
for (let i = 0; i < players.length; i++) { for (let i = 0; i < players.length; i++) {
const player = players[i] const player = players[i]
const playerBounds = document.getElementById(player.id)!.getBoundingClientRect() const playerBounds = document
.getElementById(player.id)!
.getBoundingClientRect()
const doesOverlap = !( const doesOverlap = !(
bounds.top > playerBounds.bottom || bounds.top > playerBounds.bottom ||
bounds.right < playerBounds.left || bounds.right < playerBounds.left ||
bounds.bottom < playerBounds.top || bounds.bottom < playerBounds.top ||
bounds.left > playerBounds.right bounds.left > playerBounds.right
) )
if(doesOverlap) { if (doesOverlap) {
return i return i
} }
} }
return -1 return -1
} }
const onBallDropOnPlayer = (playerCollidedIdx: number) => {
const onBallDropOnPlayer = (playerCollidedIdx : number) => {
setContent((content) => { setContent((content) => {
const ballObj = content.objects.findIndex(o => o.type == "ball") const ballObj = content.objects.findIndex((o) => o.type == "ball")
let player = content.players.at(playerCollidedIdx) as Player let player = content.players.at(playerCollidedIdx) as Player
return { return {
...content, ...content,
players: content.players.toSpliced(playerCollidedIdx, 1, {...player, hasBall: true}), players: content.players.toSpliced(playerCollidedIdx, 1, {
objects : content.objects.toSpliced(ballObj, 1) ...player,
hasBall: true,
}),
objects: content.objects.toSpliced(ballObj, 1),
} }
}) })
} }
const onBallDrop = (refBounds: DOMRect) => { const onBallDrop = (refBounds: DOMRect) => {
if (!isBoundsOnCourt(refBounds)) {
setContent((content) => {
const ballObj = content.objects.findIndex(
(o) => o.type == "ball",
)
return {
...content,
objects: content.objects.toSpliced(ballObj, 1),
}
})
setObjects([{ key: "ball" }])
}
const playerCollidedIdx = getPlayerCollided(refBounds, content.players) const playerCollidedIdx = getPlayerCollided(refBounds, content.players)
if(playerCollidedIdx != -1) { if (playerCollidedIdx != -1) {
setContent((content) => { setContent((content) => {
return { return {
...content, ...content,
players: content.players.map((player) => ({...player, hasBall: false})), players: content.players.map((player) => ({
...player,
hasBall: false,
})),
} }
}) })
onBallDropOnPlayer(playerCollidedIdx) onBallDropOnPlayer(playerCollidedIdx)
return return
} }
if(content.objects.findIndex(o => o.type == "ball") != -1) { if (content.objects.findIndex((o) => o.type == "ball") != -1) {
return return
} }
const courtBounds = courtDivContentRef.current!.getBoundingClientRect() const courtBounds = courtDivContentRef.current!.getBoundingClientRect()
const {x, y} = calculateRatio(refBounds, courtBounds) const { x, y } = calculateRatio(refBounds, courtBounds)
let courtObject: CourtObject let courtObject: CourtObject
courtObject = { courtObject = {
type: "ball", type: "ball",
rightRatio: x, rightRatio: x,
bottomRatio: y bottomRatio: y,
} }
setContent((content) => { setContent((content) => {
return { return {
...content, ...content,
players: content.players.map((player) => ({...player, hasBall: false})), players: content.players.map((player) => ({
objects: [ ...player,
...content.objects, hasBall: false,
courtObject, })),
] objects: [...content.objects, courtObject],
} }
}) })
} }
@ -313,7 +348,9 @@ function EditorView({
id="allies-rack" id="allies-rack"
objects={allies} objects={allies}
onChange={setAllies} onChange={setAllies}
canDetach={div => canDetach(div.getBoundingClientRect())} canDetach={(div) =>
isBoundsOnCourt(div.getBoundingClientRect())
}
onElementDetached={onPieceDetach} onElementDetached={onPieceDetach}
render={({ team, key }) => ( render={({ team, key }) => (
<PlayerPiece <PlayerPiece
@ -325,18 +362,24 @@ function EditorView({
)} )}
/> />
<Rack id={"objects"} <Rack
id={"objects"}
objects={objects} objects={objects}
onChange={setObjects} onChange={setObjects}
canDetach={div => canDetach(div.getBoundingClientRect())} canDetach={(div) =>
isBoundsOnCourt(div.getBoundingClientRect())
}
onElementDetached={onObjectDetach} onElementDetached={onObjectDetach}
render={renderCourtObject}/> render={renderCourtObject}
/>
<Rack <Rack
id="opponent-rack" id="opponent-rack"
objects={opponents} objects={opponents}
onChange={setOpponents} onChange={setOpponents}
canDetach={div => canDetach(div.getBoundingClientRect())} canDetach={(div) =>
isBoundsOnCourt(div.getBoundingClientRect())
}
onElementDetached={onPieceDetach} onElementDetached={onPieceDetach}
render={({ team, key }) => ( render={({ team, key }) => (
<PlayerPiece <PlayerPiece
@ -376,9 +419,7 @@ function EditorView({
player, player,
false, false,
), ),
objects: [ objects: [...content.objects],
...content.objects,
]
})) }))
let setter let setter
switch (player.team) { switch (player.team) {
@ -389,7 +430,7 @@ function EditorView({
setter = setAllies setter = setAllies
} }
if (player.hasBall) { if (player.hasBall) {
setObjects([{key: "ball"}]) setObjects([{ key: "ball" }])
} }
setter((players) => [ setter((players) => [
...players, ...players,
@ -402,13 +443,18 @@ function EditorView({
}} }}
onBallRemove={() => { onBallRemove={() => {
setContent((content) => { setContent((content) => {
const ballObj = content.objects.findIndex(o => o.type == "ball") const ballObj = content.objects.findIndex(
(o) => o.type == "ball",
)
return { return {
...content, ...content,
objects: content.objects.toSpliced(ballObj, 1) objects: content.objects.toSpliced(
ballObj,
1,
),
} }
}) })
setObjects([{key: "ball"}]) setObjects([{ key: "ball" }])
}} }}
/> />
</div> </div>
@ -418,16 +464,16 @@ function EditorView({
) )
} }
function isBallOnCourt(content : TacticContent) { function isBallOnCourt(content: TacticContent) {
if(content.players.findIndex(p => p.hasBall) != -1) { if (content.players.findIndex((p) => p.hasBall) != -1) {
return true return true
} }
return content.objects.findIndex(o => o.type == "ball") != -1 return content.objects.findIndex((o) => o.type == "ball") != -1
} }
function renderCourtObject(courtObject: RackedCourtObject) { function renderCourtObject(courtObject: RackedCourtObject) {
if (courtObject.key == "ball") { if (courtObject.key == "ball") {
return <BallPiece/> return <BallPiece />
} }
throw new Error("unknown racked court object ", courtObject.key) throw new Error("unknown racked court object ", courtObject.key)
} }

Loading…
Cancel
Save