planning-poker-gwargh/frontend/src/main/scala/industries/sunshine/planningpoker/TableView.scala

92 lines
3.3 KiB
Scala

package industries.sunshine.planningpoker
import scala.scalajs.js
import com.raquo.laminar.api.L.{*, given}
import scala.scalajs.js.Dynamic.{global => g}
import industries.sunshine.planningpoker.common.Models.*
object TableView {
// new plan. map to players, split by playerId, map into specific card
// have single funciton that calculates the card from the state.
// but, can't have map to player, because overall state is required.
// but can i split full state into several observables by that key? i think i should be able to
// so, it's more efficient to share an observable, than to create multiple copies...
def renderTable(roomStateSignal: Signal[RoomStateView]): Element = {
val playerIdToCardTypeSignal =
roomStateSignal
.map(state =>
state.players.map(p => p.id -> getPlayerCardType(p.id, state.round, p.name, state.me))
)
div(
className := "w-full h-full border-2 border-amber-700 flex flex-col justify-center items-center bg-green-100",
div(
className := "w-full border-2 border-amber-600 flex flex-row justify-center items-center bg-green-100",
children <-- playerIdToCardTypeSignal.split(_._1) { (id, initial, cardTypeSignal) =>
renderPlayerCard(cardTypeSignal.map(_._2))
}
),
child <-- roomStateSignal.map { state =>
if (state.canCloseRound) TableControls.render(roomStateSignal.map(_.round)) else emptyNode
}
)
}
trait CardState
case class NoCard(name: String) extends CardState
case class CardBack(name: String) extends CardState
case class Open(name: String, value: String) extends CardState
def getPlayerCardType(
id: PlayerID,
state: RoundStateView,
name: String,
myId: PlayerID
): CardState = {
state match {
case isOpen: RoundStateView.Voting =>
if (myId == id) {
isOpen.myCard.fold(NoCard(name))(vote => Open(name, vote))
} else isOpen.alreadyVoted.find(_ == id).fold(NoCard(name))(_ => CardBack(name))
case isClosed: RoundStateView.Viewing =>
isClosed.votes
.find(_._1 == id)
.fold {
g.console.error(s"missing vote for player $name")
NoCard(name)
} { case (_, vote) => Open(name, vote) }
}
}
def renderPlayerCard(state: Signal[CardState]): Element = {
val cardTypeStyle = state.map {
case NoCard(_) => "bg-green-100 text-black border-2 border-black"
case CardBack(_) => "bg-green-500 border-4 border-green-700"
case Open(_, _) => "text-black bg-gray-50 border-black border-2"
}
div(
className := "w-20 h-40 m-1 rounded m-3 relative",
className <-- cardTypeStyle,
// the diagonal card value \ place text
div(
className := "-rotate-45 text-xl absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
child.text <-- state.map {
case NoCard(name) => name
case CardBack(name) => name
case Open(_, vote) => vote
}
),
// name under viewing the votes
div(
className := "text-xl absolute bottom-1 left-1/2 transform -translate-x-1/2",
child.text <-- state.map {
case Open(name, _) => name
case _ => ""
}
)
)
}
}