From 3dab3f66b9c5bcbf1bfaa31fbbc5d94457e5f2f4 Mon Sep 17 00:00:00 2001 From: efim Date: Sun, 7 May 2023 18:25:54 +0400 Subject: [PATCH] feat: styling cards as fan --- .../sunshine/planningpoker/OtherPlayers.scala | 20 ++++++++--- .../planningpoker/OwnHandControls.scala | 33 ++++++++++++++----- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/frontend/src/main/scala/industries/sunshine/planningpoker/OtherPlayers.scala b/frontend/src/main/scala/industries/sunshine/planningpoker/OtherPlayers.scala index 9bd0799..7be313f 100644 --- a/frontend/src/main/scala/industries/sunshine/planningpoker/OtherPlayers.scala +++ b/frontend/src/main/scala/industries/sunshine/planningpoker/OtherPlayers.scala @@ -34,22 +34,32 @@ object OtherPlayers { val xOffsetStyleSignal = p.map(_._2) div( className := "bg-green-200 border-b-2 border-black rounded p-2 m-2 absolute drop-shadow-xl m-3", - // left <-- p.map(tuple => ((1 + tuple._2) * 10000).toString()), styleAttr <-- p.map(tuple => s"left: ${(0 + tuple._2) * 200}px"), child.text <-- p.map(_._1.name), renderHandCardBacks(cardsAmount) ) } + def dynamicCardStyle(index: Int, totalCards: Int): String = { + val offCenterIndex = 2*index - (totalCards + 1) + val maxOffCenter = (totalCards + 1) / 2 + val angle = Math.round((offCenterIndex / maxOffCenter.toDouble) * 20) + val xOffset = (offCenterIndex * 0.3) + s"transform: rotate(${angle}deg) translateX(${xOffset}em) " + } + def renderHandCardBacks(amountSignal: Signal[Int]): Element = { - def renderCard(index: Int): Element = + def renderCard(index: Int, totalCards: Int): Element = div( - className := "w-8 h-12 rounded bg-green-500 text-yellow m-1 border border-green-700 drop-shadow-md" + className := "w-8 h-12 rounded bg-green-500 text-yellow m-1 border border-green-700 drop-shadow-md", + className := "absolute ", + styleAttr := dynamicCardStyle(index, totalCards), + ) div( - className := "flex flex-row", - children <-- amountSignal.map { amount => (1 to amount).map(renderCard) } + className := "relative h-16 w-20", + children <-- amountSignal.map { amount => (1 to amount).map(renderCard(_, amount)) } ) } diff --git a/frontend/src/main/scala/industries/sunshine/planningpoker/OwnHandControls.scala b/frontend/src/main/scala/industries/sunshine/planningpoker/OwnHandControls.scala index 987f8a7..6135b8b 100644 --- a/frontend/src/main/scala/industries/sunshine/planningpoker/OwnHandControls.scala +++ b/frontend/src/main/scala/industries/sunshine/planningpoker/OwnHandControls.scala @@ -12,32 +12,47 @@ object OwnHandControls { def render(roomStateSignal: Signal[RoomStateView]): Element = { val cardTypesSignal = roomStateSignal.map(myUnselectedCards(_)) div( - className := "flex flex-row justify-center", - children <-- cardTypesSignal.map(_.map(renderCard(_))) + className := " h-1/3 relative", + children <-- cardTypesSignal.map(cards => cards.map(renderCard(_, cards.size))), ) } - private def renderCard(value: String): Element = { + def dynamicCardStyle(index: Int, totalCards: Int): String = { + val offCenterIndex = 2*index - (totalCards + 1) + val maxOffCenter = (totalCards + 1) / 2 + val angle = Math.round((offCenterIndex / maxOffCenter.toDouble) * 15) + val xOffset = offCenterIndex * 1.3 + s"$offCenterIndex ${(offCenterIndex.toLong / maxOffCenter.toLong)} transform: rotate(${angle}deg) " + s"transform: rotate(${angle}deg) translateX(${xOffset}em) translateY(-2em)" + } + + private def renderCard(cardWithIndex: (String, Int), totalCards: Int): Element = { + val (value, index) = cardWithIndex val submitVote = Fetch.get(s"/api/vote/$value").text div( className := "cursor-pointer w-24 h-48 m-1 rounded-l flex justify-center items-center m-3 text-black bg-gray-50 border-black border-2 drop-shadow-md", - className := "hover:-translate-y-2 hover:scale-[1.02] hover:drop-shadow-xl rounded-lg hover:bg-gray-100 ease-linear duration-200", + className := " hover:z-50 hover:drop-shadow-xl rounded-lg hover:bg-gray-100 ease-linear duration-200", + className := "absolute origin-bottom start-1/2", + styleAttr := dynamicCardStyle(index, totalCards), onClick.flatMap(_ => submitVote) --> Observer(resp => g.console.info(resp.toString())), div( className := "-rotate-45 text-xl", - value + value, ) ) } - private def myUnselectedCards(state: RoomStateView): List[String] = { - state.round match { + private def myUnselectedCards(state: RoomStateView): List[(String, Int)] = { + val allCards = state.allowedCards + val cards = state.round match { case RoundStateView.Voting(myCard, _) => - state.allowedCards.filterNot(value => myCard.contains(value)) + allCards.filterNot(value => myCard.contains(value)) case RoundStateView.Viewing(votes) => - state.allowedCards.filterNot(value => votes.toMap.get(state.me).contains(value)) + allCards.filterNot(value => votes.toMap.get(state.me).contains(value)) } + + cards.zip(1 to state.allowedCards.size) } }