summaryrefslogtreecommitdiff
path: root/sw-ui
diff options
context:
space:
mode:
authorJoffrey Bion <joffrey.bion@booking.com>2020-05-26 02:39:20 +0200
committerJoffrey Bion <joffrey.bion@booking.com>2020-05-26 02:40:44 +0200
commitd26ecccf690d33c3cf71412395a257ebb40d29e7 (patch)
tree70349cd50bd5812e0895af11c536fb096472e2da /sw-ui
parentFix player positions in lobby (diff)
downloadseven-wonders-d26ecccf690d33c3cf71412395a257ebb40d29e7.tar.gz
seven-wonders-d26ecccf690d33c3cf71412395a257ebb40d29e7.tar.bz2
seven-wonders-d26ecccf690d33c3cf71412395a257ebb40d29e7.zip
Add other players' board summary
Resolves: https://github.com/joffrey-bion/seven-wonders/issues/21
Diffstat (limited to 'sw-ui')
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Board.kt1
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/BoardSummary.kt117
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt62
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/ProductionBar.kt47
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Tokens.kt113
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt2
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt86
-rw-r--r--sw-ui/src/main/resources/images/tokens/cog.pngbin0 -> 13071 bytes
-rw-r--r--sw-ui/src/main/resources/images/tokens/compass.pngbin0 -> 9720 bytes
-rw-r--r--sw-ui/src/main/resources/images/tokens/laurel-blue.pngbin0 -> 13534 bytes
-rw-r--r--sw-ui/src/main/resources/images/tokens/military.pngbin0 -> 17253 bytes
-rw-r--r--sw-ui/src/main/resources/images/tokens/tablet.pngbin0 -> 12438 bytes
12 files changed, 350 insertions, 78 deletions
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Board.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Board.kt
index c5d4dd4a..bd6a9c2f 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Board.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Board.kt
@@ -25,6 +25,7 @@ private const val yOffset = 21
fun RBuilder.boardComponent(board: Board) {
styledDiv {
css {
+ paddingTop = 3.rem
width = 100.vw
}
tableCards(cardColumns = board.playedCards)
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/BoardSummary.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/BoardSummary.kt
new file mode 100644
index 00000000..f6225765
--- /dev/null
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/BoardSummary.kt
@@ -0,0 +1,117 @@
+package org.luxons.sevenwonders.ui.components.game
+
+import kotlinx.css.*
+import kotlinx.html.DIV
+import org.luxons.sevenwonders.model.api.PlayerDTO
+import org.luxons.sevenwonders.model.boards.Board
+import org.luxons.sevenwonders.ui.components.gameBrowser.playerInfo
+import react.RBuilder
+import styled.StyledDOMBuilder
+import styled.css
+import styled.styledDiv
+import styled.styledHr
+
+enum class BoardSummarySide(
+ val tokenCountPosition: TokenCountPosition,
+ val scienceCountPosition: TokenCountPosition,
+ val alignment: Align
+) {
+ LEFT(TokenCountPosition.RIGHT, TokenCountPosition.RIGHT, Align.flexStart),
+ TOP(TokenCountPosition.OVER, TokenCountPosition.OVER, Align.flexStart),
+ RIGHT(TokenCountPosition.LEFT, TokenCountPosition.LEFT, Align.flexEnd)
+}
+
+fun RBuilder.boardSummary(
+ player: PlayerDTO,
+ board: Board,
+ boardSummarySide: BoardSummarySide,
+ block: StyledDOMBuilder<DIV>.() -> Unit = {}
+) {
+ styledDiv {
+ css {
+ display = Display.flex
+ flexDirection = FlexDirection.column
+ alignItems = boardSummarySide.alignment
+ padding(all = 0.5.rem)
+ backgroundColor = Color.paleGoldenrod.withAlpha(0.5)
+ }
+ val tokenSize = 2.rem
+ val countPosition = boardSummarySide.tokenCountPosition
+
+ playerInfo(player, iconSize = 25)
+ styledHr {
+ css {
+ margin(vertical = 0.5.rem)
+ width = 100.pct
+ }
+ }
+ styledDiv {
+ css {
+ display = Display.flex
+ flexDirection = if (boardSummarySide == BoardSummarySide.TOP) FlexDirection.row else FlexDirection.column
+ alignItems = boardSummarySide.alignment
+ }
+ styledDiv {
+ css {
+ display = Display.flex
+ flexDirection = if (boardSummarySide == BoardSummarySide.TOP) FlexDirection.row else FlexDirection.column
+ alignItems = boardSummarySide.alignment
+ if (boardSummarySide == BoardSummarySide.TOP) marginRight = 1.rem else marginBottom = 1.rem
+ }
+ generalCounts(board, tokenSize, countPosition)
+ }
+ scienceTokens(board, tokenSize, boardSummarySide.scienceCountPosition)
+ }
+ block()
+ }
+}
+
+private fun StyledDOMBuilder<DIV>.generalCounts(
+ board: Board,
+ tokenSize: LinearDimension,
+ countPosition: TokenCountPosition
+) {
+ goldIndicator(amount = board.gold, imgSize = tokenSize, amountPosition = countPosition)
+ tokenWithCount(
+ tokenName = "laurel-blue",
+ count = board.bluePoints,
+ imgSize = tokenSize,
+ countPosition = countPosition,
+ brightText = countPosition == TokenCountPosition.OVER
+ )
+ tokenWithCount(
+ tokenName = "military",
+ count = board.military.nbShields,
+ imgSize = tokenSize,
+ countPosition = countPosition,
+ brightText = countPosition == TokenCountPosition.OVER
+ )
+}
+
+private fun RBuilder.scienceTokens(
+ board: Board,
+ tokenSize: LinearDimension,
+ sciencePosition: TokenCountPosition
+) {
+ tokenWithCount(
+ tokenName = "compass",
+ count = board.science.nbCompasses,
+ imgSize = tokenSize,
+ countPosition = sciencePosition,
+ brightText = sciencePosition == TokenCountPosition.OVER
+ )
+ tokenWithCount(
+ tokenName = "cog",
+ count = board.science.nbWheels,
+ imgSize = tokenSize,
+ countPosition = sciencePosition,
+ brightText = sciencePosition == TokenCountPosition.OVER
+ )
+ tokenWithCount(
+ tokenName = "tablet",
+ count = board.science.nbTablets,
+ imgSize = tokenSize,
+ countPosition = sciencePosition,
+ brightText = sciencePosition == TokenCountPosition.OVER
+ )
+}
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
index be6b0e50..64077359 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/GameScene.kt
@@ -3,10 +3,10 @@ package org.luxons.sevenwonders.ui.components.game
import com.palantir.blueprintjs.*
import kotlinx.css.*
import kotlinx.css.properties.*
-import org.luxons.sevenwonders.model.Action
-import org.luxons.sevenwonders.model.PlayerMove
-import org.luxons.sevenwonders.model.PlayerTurnInfo
+import org.luxons.sevenwonders.model.*
import org.luxons.sevenwonders.model.api.PlayerDTO
+import org.luxons.sevenwonders.model.boards.Board
+import org.luxons.sevenwonders.model.boards.RelativeBoardPosition
import org.luxons.sevenwonders.model.cards.HandCard
import org.luxons.sevenwonders.ui.components.GlobalStyles
import org.luxons.sevenwonders.ui.redux.*
@@ -52,13 +52,21 @@ private class GameScene(props: GameSceneProps) : RComponent<GameSceneProps, RSta
}
private fun RBuilder.turnInfoScene(turnInfo: PlayerTurnInfo) {
- val board = turnInfo.table.boards[turnInfo.playerIndex]
+ val board = turnInfo.getOwnBoard()
+ val leftBoard = turnInfo.getBoard(RelativeBoardPosition.LEFT)
+ val rightBoard = turnInfo.getBoard(RelativeBoardPosition.RIGHT)
+ val topBoards = (turnInfo.table.boards - board - leftBoard - rightBoard).reversed()
div {
turnInfo.scoreBoard?.let {
scoreTableOverlay(it, props.players, props.leaveGame)
}
actionInfo(turnInfo.message)
boardComponent(board = board)
+ leftPlayerBoardSummary(leftBoard)
+ rightPlayerBoardSummary(rightBoard)
+ if (topBoards.isNotEmpty()) {
+ topPlayerBoardsSummaries(topBoards)
+ }
handRotationIndicator(turnInfo.table.handRotationDirection)
val hand = turnInfo.hand
if (hand != null) {
@@ -97,6 +105,52 @@ private class GameScene(props: GameSceneProps) : RComponent<GameSceneProps, RSta
}
}
+ private fun RBuilder.leftPlayerBoardSummary(board: Board) {
+ boardSummary(props.players[board.playerIndex], board, BoardSummarySide.LEFT) {
+ css {
+ position = Position.absolute
+ left = 0.px
+ bottom = 40.pct
+ borderTopRightRadius = 0.4.rem
+ borderBottomRightRadius = 0.4.rem
+ }
+ }
+ }
+
+ private fun RBuilder.rightPlayerBoardSummary(board: Board) {
+ boardSummary(props.players[board.playerIndex], board, BoardSummarySide.RIGHT) {
+ css {
+ position = Position.absolute
+ right = 0.px
+ bottom = 40.pct
+ borderTopLeftRadius = 0.4.rem
+ borderBottomLeftRadius = 0.4.rem
+ }
+ }
+ }
+
+ private fun RBuilder.topPlayerBoardsSummaries(boards: List<Board>) {
+ styledDiv {
+ css {
+ position = Position.absolute
+ top = 0.px
+ left = 50.pct
+ transform { translate((-50).pct) }
+ display = Display.flex
+ flexDirection = FlexDirection.row
+ }
+ boards.forEach { board ->
+ boardSummary(props.players[board.playerIndex], board, BoardSummarySide.TOP) {
+ css {
+ borderBottomLeftRadius = 0.4.rem
+ borderBottomRightRadius = 0.4.rem
+ margin(horizontal = 2.rem)
+ }
+ }
+ }
+ }
+ }
+
private fun RBuilder.preparedMove(card: HandCard, move: PlayerMove) {
bpOverlay(isOpen = true, onClose = props.unprepareMove) {
preparedMove(card, move, props.unprepareMove) {
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/ProductionBar.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/ProductionBar.kt
index 27a77ba6..d3243906 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/ProductionBar.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/ProductionBar.kt
@@ -12,7 +12,6 @@ import kotlinx.css.background
import kotlinx.css.bottom
import kotlinx.css.color
import kotlinx.css.display
-import kotlinx.css.fontFamily
import kotlinx.css.fontSize
import kotlinx.css.height
import kotlinx.css.margin
@@ -27,8 +26,6 @@ import kotlinx.css.vw
import kotlinx.css.width
import kotlinx.css.zIndex
import kotlinx.html.DIV
-import kotlinx.html.IMG
-import kotlinx.html.title
import org.luxons.sevenwonders.model.boards.Production
import org.luxons.sevenwonders.model.resources.CountedResource
import org.luxons.sevenwonders.model.resources.ResourceType
@@ -37,7 +34,6 @@ import react.dom.*
import styled.StyledDOMBuilder
import styled.css
import styled.styledDiv
-import styled.styledImg
import styled.styledSpan
fun RBuilder.productionBar(gold: Int, production: Production) {
@@ -51,10 +47,6 @@ fun RBuilder.productionBar(gold: Int, production: Production) {
}
}
-private fun RBuilder.goldIndicator(amount: Int) {
- tokenWithCount(tokenName = "coin", count = amount)
-}
-
private fun RBuilder.fixedResources(resources: List<CountedResource>) {
styledDiv {
css {
@@ -106,32 +98,6 @@ private fun RBuilder.resourceChoice(types: Set<ResourceType>, block: StyledDOMBu
}
}
-private fun RBuilder.tokenWithCount(tokenName: String, count: Int, block: StyledDOMBuilder<DIV>.() -> Unit = {}) {
- styledDiv {
- block()
- tokenImage(tokenName)
- styledSpan {
- css { tokenCountStyle() }
- + "× $count"
- }
- }
-}
-
-private fun RBuilder.tokenImage(tokenName: String, block: StyledDOMBuilder<IMG>.() -> Unit = {}) {
- styledImg(src = getTokenImagePath(tokenName)) {
- css {
- tokenImageStyle()
- }
- attrs {
- this.title = tokenName
- this.alt = tokenName
- }
- block()
- }
-}
-
-private fun getTokenImagePath(tokenName: String) = "/images/tokens/$tokenName.png"
-
private fun getResourceTokenName(resourceType: ResourceType) = "resources/${resourceType.toString().toLowerCase()}"
private fun CSSBuilder.productionBarStyle() {
@@ -154,16 +120,3 @@ private fun CSSBuilder.choiceSeparatorStyle() {
color = Color("#c29929")
declarations["text-shadow"] = "0 0 1px black"
}
-
-private fun CSSBuilder.tokenImageStyle() {
- height = 3.rem
- width = 3.rem
- verticalAlign = VerticalAlign.middle
-}
-
-private fun CSSBuilder.tokenCountStyle() {
- fontFamily = "fantasy"
- fontSize = 1.5.rem
- verticalAlign = VerticalAlign.middle
- marginLeft = 0.2.rem
-}
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Tokens.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Tokens.kt
new file mode 100644
index 00000000..e9425e0a
--- /dev/null
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/game/Tokens.kt
@@ -0,0 +1,113 @@
+package org.luxons.sevenwonders.ui.components.game
+
+import kotlinx.css.*
+import kotlinx.html.DIV
+import kotlinx.html.IMG
+import kotlinx.html.title
+import org.luxons.sevenwonders.ui.components.GlobalStyles
+import react.RBuilder
+import styled.*
+
+enum class TokenCountPosition {
+ LEFT, RIGHT, OVER
+}
+
+fun RBuilder.goldIndicator(
+ amount: Int,
+ amountPosition: TokenCountPosition = TokenCountPosition.OVER,
+ imgSize: LinearDimension = 3.rem
+) {
+ tokenWithCount(
+ tokenName = "coin",
+ title = "$amount gold coins",
+ imgSize = imgSize,
+ count = amount,
+ countPosition = amountPosition
+ )
+}
+
+fun RBuilder.tokenWithCount(
+ tokenName: String,
+ count: Int,
+ title: String = tokenName,
+ imgSize: LinearDimension = 3.rem,
+ countPosition: TokenCountPosition = TokenCountPosition.RIGHT,
+ brightText: Boolean = false,
+ block: StyledDOMBuilder<DIV>.() -> Unit = {}
+) {
+ styledDiv {
+ block()
+ val tokenCountSize = imgSize * 0.6
+ when (countPosition) {
+ TokenCountPosition.RIGHT -> {
+ tokenImage(tokenName, title = title, size = imgSize)
+ styledSpan {
+ css {
+ tokenCountStyle(tokenCountSize, brightText)
+ marginLeft = 0.2.rem
+ }
+ +"× $count"
+ }
+ }
+ TokenCountPosition.LEFT -> {
+ styledSpan {
+ css {
+ tokenCountStyle(tokenCountSize, brightText)
+ marginRight = 0.2.rem
+ }
+ +"$count ×"
+ }
+ tokenImage(tokenName, title = title, size = imgSize)
+ }
+ TokenCountPosition.OVER -> {
+ styledDiv {
+ css { position = Position.relative }
+ tokenImage(tokenName, title = title, size = imgSize)
+ styledSpan {
+ css {
+ +GlobalStyles.centerInParent
+ tokenCountStyle(tokenCountSize, brightText)
+ }
+ +"$count"
+ }
+ }
+ }
+ }
+ }
+}
+
+fun RBuilder.tokenImage(
+ tokenName: String,
+ title: String = tokenName,
+ size: LinearDimension = 3.rem,
+ block: StyledDOMBuilder<IMG>.() -> Unit = {}
+) {
+ styledImg(src = getTokenImagePath(tokenName)) {
+ css {
+ tokenImageStyle(size)
+ }
+ attrs {
+ this.title = title
+ this.alt = tokenName
+ }
+ block()
+ }
+}
+
+private fun getTokenImagePath(tokenName: String) = "/images/tokens/$tokenName.png"
+
+private fun CSSBuilder.tokenImageStyle(size: LinearDimension) {
+ height = size
+ width = size
+ verticalAlign = VerticalAlign.middle
+}
+
+private fun CSSBuilder.tokenCountStyle(size: LinearDimension, brightText: Boolean) {
+ fontFamily = "fantasy"
+ fontSize = size
+ verticalAlign = VerticalAlign.middle
+ if (brightText) {
+ color = Color.white
+ }
+ fontWeight = FontWeight.bold
+}
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt
index 82e1ae1a..6b04801e 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt
@@ -52,7 +52,7 @@ private class CreateGameForm(props: CreateGameFormProps) : RComponent<CreateGame
rightElement = createGameButton()
)
}
- playerInfo()
+ currentPlayerInfo()
}
}
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt
index 62b9f7aa..a6289649 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/PlayerInfo.kt
@@ -2,7 +2,8 @@ package org.luxons.sevenwonders.ui.components.gameBrowser
import com.palantir.blueprintjs.bpIcon
import kotlinx.css.*
-import org.luxons.sevenwonders.model.api.ConnectedPlayer
+import org.luxons.sevenwonders.model.api.BasicPlayerInfo
+import org.luxons.sevenwonders.model.api.PlayerDTO
import org.luxons.sevenwonders.ui.redux.connectState
import react.RBuilder
import react.RComponent
@@ -13,7 +14,9 @@ import styled.styledDiv
import styled.styledSpan
interface PlayerInfoProps : RProps {
- var connectedPlayer: ConnectedPlayer?
+ var player: BasicPlayerInfo?
+ var showUsername: Boolean
+ var iconSize: Int
}
class PlayerInfoPresenter(props: PlayerInfoProps) : RComponent<PlayerInfoProps, RState>(props) {
@@ -24,39 +27,70 @@ class PlayerInfoPresenter(props: PlayerInfoProps) : RComponent<PlayerInfoProps,
display = Display.flex
alignItems = Align.center
}
- props.connectedPlayer?.let {
- bpIcon(name = it.icon?.name ?: "user", size = 30)
- styledDiv {
- css {
- display = Display.flex
- flexDirection = FlexDirection.column
- marginLeft = 0.3.rem
- }
- styledSpan {
- css {
- fontSize = 1.rem
- }
- +it.displayName
- }
- styledSpan {
- css {
- marginTop = 0.1.rem
- color = Color.lightGray
- fontSize = 0.8.rem
- }
- +"(${it.username})"
- }
+ props.player?.let {
+ bpIcon(name = it.icon?.name ?: "user", size = props.iconSize)
+ if (props.showUsername) {
+ playerNameWithUsername(it.displayName, it.username)
+ } else {
+ playerName(it.displayName)
}
}
}
}
+
+ private fun RBuilder.playerName(displayName: String) {
+ styledSpan {
+ css {
+ fontSize = 1.rem
+ marginLeft = 0.4.rem
+ }
+ +displayName
+ }
+ }
+
+ private fun RBuilder.playerNameWithUsername(displayName: String, username: String) {
+ styledDiv {
+ css {
+ display = Display.flex
+ flexDirection = FlexDirection.column
+ marginLeft = 0.4.rem
+ }
+ styledSpan {
+ css {
+ fontSize = 1.rem
+ }
+ +displayName
+ }
+ styledSpan {
+ css {
+ marginTop = 0.1.rem
+ color = Color.lightGray
+ fontSize = 0.8.rem
+ }
+ +"($username)"
+ }
+ }
+ }
+}
+
+fun RBuilder.playerInfo(
+ playerDTO: PlayerDTO,
+ showUsername: Boolean = false,
+ iconSize: Int = 30
+) = child(PlayerInfoPresenter::class) {
+ attrs {
+ this.player = playerDTO
+ this.showUsername = showUsername
+ this.iconSize = iconSize
+ }
}
-fun RBuilder.playerInfo() = playerInfo {}
+fun RBuilder.currentPlayerInfo() = playerInfo {}
private val playerInfo = connectState(
clazz = PlayerInfoPresenter::class,
mapStateToProps = { state, _ ->
- connectedPlayer = state.connectedPlayer
+ player = state.connectedPlayer
+ showUsername = true
}
)
diff --git a/sw-ui/src/main/resources/images/tokens/cog.png b/sw-ui/src/main/resources/images/tokens/cog.png
new file mode 100644
index 00000000..61250d8a
--- /dev/null
+++ b/sw-ui/src/main/resources/images/tokens/cog.png
Binary files differ
diff --git a/sw-ui/src/main/resources/images/tokens/compass.png b/sw-ui/src/main/resources/images/tokens/compass.png
new file mode 100644
index 00000000..6497e34f
--- /dev/null
+++ b/sw-ui/src/main/resources/images/tokens/compass.png
Binary files differ
diff --git a/sw-ui/src/main/resources/images/tokens/laurel-blue.png b/sw-ui/src/main/resources/images/tokens/laurel-blue.png
new file mode 100644
index 00000000..115bba91
--- /dev/null
+++ b/sw-ui/src/main/resources/images/tokens/laurel-blue.png
Binary files differ
diff --git a/sw-ui/src/main/resources/images/tokens/military.png b/sw-ui/src/main/resources/images/tokens/military.png
new file mode 100644
index 00000000..3a0e1dea
--- /dev/null
+++ b/sw-ui/src/main/resources/images/tokens/military.png
Binary files differ
diff --git a/sw-ui/src/main/resources/images/tokens/tablet.png b/sw-ui/src/main/resources/images/tokens/tablet.png
new file mode 100644
index 00000000..954fd9ef
--- /dev/null
+++ b/sw-ui/src/main/resources/images/tokens/tablet.png
Binary files differ
bgstack15