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 _ => "" } ) ) } }