From 7730a8ad565c167029681fd15d98be6c7dce0492 Mon Sep 17 00:00:00 2001 From: Joffrey Bion Date: Sun, 22 Mar 2020 23:06:19 +0100 Subject: Migrate game browser to blueprint components --- sw-ui-kt/src/main/kotlin/blueprintjs.kt | 73 ++++++++++++++++++++- sw-ui-kt/src/main/kotlin/blueprintjsHelpers.kt | 46 +++++++------ .../ui/components/gameBrowser/CreateGameForm.kt | 53 +++++++++++---- .../ui/components/gameBrowser/GameList.kt | 76 +++++++++++++--------- .../ui/components/home/ChooseNameForm.kt | 4 +- .../ui/components/lobby/RadialPlayerList.kt | 4 +- 6 files changed, 182 insertions(+), 74 deletions(-) diff --git a/sw-ui-kt/src/main/kotlin/blueprintjs.kt b/sw-ui-kt/src/main/kotlin/blueprintjs.kt index 37892fde..81227835 100644 --- a/sw-ui-kt/src/main/kotlin/blueprintjs.kt +++ b/sw-ui-kt/src/main/kotlin/blueprintjs.kt @@ -1,8 +1,6 @@ @file:JsModule("@blueprintjs/core") package com.palantir.blueprintjs -import org.w3c.dom.HTMLAnchorElement -import org.w3c.dom.HTMLButtonElement import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLInputElement import org.w3c.dom.events.Event @@ -158,6 +156,8 @@ external class Icon : PureComponent { } external interface IButtonProps : IActionProps { + // artificially added to allow title on button (should probably be on more general props) + var title: String? /** * If set to `true`, the button will display in an active state. * This is equivalent to setting `className={Classes.ACTIVE}`. @@ -258,3 +258,72 @@ external interface IInputGroupState : RState { external class InputGroup : PureComponent { override fun render(): ReactElement } + +external interface ITagProps : IProps, IIntentProps { + /** + * Whether the tag should appear in an active state. + * @default false + */ + var active: Boolean? + /** + * Whether the tag should take up the full width of its container. + * @default false + */ + var fill: Boolean? + /** Name of a Blueprint UI icon (or an icon element) to render before the children. */ + var icon: IconName? + /** + * Whether the tag should visually respond to user interactions. If set + * to `true`, hovering over the tag will change its color and mouse cursor. + * + * Recommended when `onClick` is also defined. + * + * @default false + */ + var interactive: Boolean? + /** + * Whether this tag should use large styles. + * @default false + */ + var large: Boolean? + /** + * Whether this tag should use minimal styles. + * @default false + */ + var minimal: Boolean? + /** + * Whether tag content should be allowed to occupy multiple lines. + * If false, a single line of text will be truncated with an ellipsis if + * it overflows. Note that icons will be vertically centered relative to + * multiline text. + * @default false + */ + var multiline: Boolean? + /** + * Callback invoked when the tag is clicked. + * Recommended when `interactive` is `true`. + */ + var onClick: ((e: MouseEvent) -> Unit)?; + /** + * Click handler for remove button. + * The remove button will only be rendered if this prop is defined. + */ + var onRemove: ((e: MouseEvent, tagProps: ITagProps) -> Unit)? + /** Name of a Blueprint UI icon (or an icon element) to render after the children. */ + var rightIcon: IconName? + /** + * Whether this tag should have rounded ends. + * @default false + */ + var round: Boolean? +} + +external class Tag : PureComponent { + override fun render(): ReactElement +} + +external class Classes { + companion object { + val HTML_TABLE: String = definedExternally + } +} diff --git a/sw-ui-kt/src/main/kotlin/blueprintjsHelpers.kt b/sw-ui-kt/src/main/kotlin/blueprintjsHelpers.kt index 5344b64e..0d9cb9bd 100644 --- a/sw-ui-kt/src/main/kotlin/blueprintjsHelpers.kt +++ b/sw-ui-kt/src/main/kotlin/blueprintjsHelpers.kt @@ -1,21 +1,21 @@ package com.palantir.blueprintjs -import org.w3c.dom.HTMLElement import org.w3c.dom.events.Event import org.w3c.dom.events.MouseEvent import react.RBuilder -import react.RElementBuilder import react.RHandler import react.ReactElement typealias IconName = String -fun RBuilder.icon( +fun RBuilder.bpIcon( name: IconName, size: Int = Icon.SIZE_STANDARD, intent: Intent = Intent.NONE, title: String? = null, - alt: String? = null + alt: String? = null, + className: String? = null, + block: RHandler = {} ): ReactElement = child(Icon::class) { attrs { this.icon = name @@ -23,13 +23,16 @@ fun RBuilder.icon( this.htmlTitle = title this.intent = intent this.title = alt + this.className = className } + block() } fun RBuilder.bpButton( minimal: Boolean = false, large: Boolean = false, disabled: Boolean = false, + title: String? = null, icon: IconName? = null, rightIcon: IconName? = null, intent: Intent = Intent.NONE, @@ -37,6 +40,7 @@ fun RBuilder.bpButton( block: RHandler = {} ): ReactElement = child(Button::class) { attrs { + this.title = title this.minimal = minimal this.large = large this.disabled = disabled @@ -48,7 +52,7 @@ fun RBuilder.bpButton( block() } -fun RBuilder.inputGroup( +fun RBuilder.bpInputGroup( large: Boolean = false, placeholder: String = "", rightElement: ReactElement? = null, @@ -62,22 +66,16 @@ fun RBuilder.inputGroup( } } -data class ButtonProps( - override var className: String? = null, - override var intent: Intent? = Intent.NONE, - override var disabled: Boolean? = false, - override var icon: IconName? = null, - override var onClick: ((event: MouseEvent) -> Unit)? = null, - override var text: String? = null, - override var active: Boolean? = true, - override var alignText: Alignment? = null, - override var elementRef: ((ref: HTMLElement?) -> Any)? = null, - override var fill: Boolean? = null, - override var large: Boolean? = null, - override var loading: Boolean? = null, - override var minimal: Boolean? = null, - override var outlined: Boolean? = null, - override var rightIcon: IconName? = null, - override var small: Boolean? = null, - override var type: String? = null -) : IButtonProps +fun RBuilder.bpTag( + intent: Intent? = null, + minimal: Boolean? = null, + active: Boolean? = null, + block: RHandler = {} +): ReactElement = child(Tag::class) { + attrs { + this.intent = intent + this.minimal = minimal + this.active = active + } + block() +} diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt index 318f49f4..c1bbf460 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/CreateGameForm.kt @@ -1,17 +1,28 @@ package org.luxons.sevenwonders.ui.components.gameBrowser -import kotlinx.html.InputType -import kotlinx.html.js.onChangeFunction +import com.palantir.blueprintjs.Intent +import com.palantir.blueprintjs.bpButton +import com.palantir.blueprintjs.bpInputGroup +import com.palantir.blueprintjs.org.luxons.sevenwonders.ui.utils.createElement +import kotlinx.css.Display +import kotlinx.css.FlexDirection +import kotlinx.css.JustifyContent +import kotlinx.css.display +import kotlinx.css.flexDirection +import kotlinx.css.justifyContent import kotlinx.html.js.onSubmitFunction import org.luxons.sevenwonders.ui.redux.RequestCreateGameAction import org.luxons.sevenwonders.ui.redux.connectDispatch import org.w3c.dom.HTMLInputElement +import org.w3c.dom.events.Event import react.RBuilder import react.RClass import react.RComponent import react.RProps import react.RState import react.dom.* +import styled.css +import styled.styledDiv private interface CreateGameFormProps: RProps { var createGame: (String) -> Unit @@ -26,22 +37,40 @@ private class CreateGameForm(props: CreateGameFormProps): RComponent + onSubmitFunction = { e -> createGame(e) } + } + + bpInputGroup( + placeholder = "Game name", + onChange = { e -> val input = e.currentTarget as HTMLInputElement setState(transformState = { CreateGameFormState(input.value) }) - } - } + }, + rightElement = createGameButton() + ) } - - input(type = InputType.submit) {} + // TODO + div { +"PLAYER INFO HERE" } + //playerInfo() } } + + private fun createGameButton() = createElement { + bpButton(minimal = true, intent = Intent.PRIMARY, icon = "add", onClick = { e -> createGame(e) }) + } + + private fun createGame(e: Event) { + e.preventDefault() // prevents refreshing the page when pressing Enter + props.createGame(state.gameName) + } } val createGameForm: RClass = connectDispatch(CreateGameForm::class) { dispatch, _ -> diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt index 206ef749..e32409e4 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/gameBrowser/GameList.kt @@ -1,8 +1,19 @@ package org.luxons.sevenwonders.ui.components.gameBrowser +import com.palantir.blueprintjs.Classes +import com.palantir.blueprintjs.Intent +import com.palantir.blueprintjs.bpButton +import com.palantir.blueprintjs.bpIcon +import com.palantir.blueprintjs.bpTag +import kotlinx.css.Align +import kotlinx.css.Display +import kotlinx.css.FlexDirection import kotlinx.css.VerticalAlign +import kotlinx.css.alignItems +import kotlinx.css.display +import kotlinx.css.flexDirection import kotlinx.css.verticalAlign -import kotlinx.html.js.onClickFunction +import kotlinx.html.classes import kotlinx.html.title import org.luxons.sevenwonders.model.api.LobbyDTO import org.luxons.sevenwonders.model.api.State @@ -14,7 +25,9 @@ import react.RProps import react.RState import react.dom.* import styled.css +import styled.styledDiv import styled.styledSpan +import styled.styledTr interface GameListStateProps : RProps { var games: List @@ -30,6 +43,9 @@ class GameListPresenter(props: GameListProps) : RComponent Unit) = tr { +private fun RBuilder.gameListItemRow(lobby: LobbyDTO, joinGame: (Long) -> Unit) = styledTr { + css { + verticalAlign = VerticalAlign.middle + } attrs { key = lobby.id.toString() } - th { +lobby.name } - th { gameStatus(lobby.state) } - th { playerCount(lobby.players.size) } - th { joinButton(lobby, joinGame) } + td { +lobby.name } + td { gameStatus(lobby.state) } + td { playerCount(lobby.players.size) } + td { joinButton(lobby, joinGame) } } private fun RBuilder.gameStatus(state: State) { - // TODO - // {state} - // const statusIntents = { - // 'LOBBY': Intent.SUCCESS, - // 'PLAYING': Intent.WARNING, - // }; - span { + val intent = when(state) { + State.LOBBY -> Intent.SUCCESS + State.PLAYING -> Intent.WARNING + } + bpTag(minimal = true, intent = intent) { +state.toString() } } private fun RBuilder.playerCount(nPlayers: Int) { - //
- // - // {nbPlayers} - //
; - - // CSS: - // .playerCountIcon, .playerCount { - // vertical-align: middle; - // } - div { + styledDiv { + css { + display = Display.flex + flexDirection = FlexDirection.row + alignItems = Align.center + } attrs { title = "Number of players" } + bpIcon(name = "people", title = null) styledSpan { - css { - verticalAlign = VerticalAlign.middle - } +nPlayers.toString() } } } private fun RBuilder.joinButton(lobby: LobbyDTO, joinGame: (Long) -> Unit) { - button { - attrs { - disabled = lobby.state != State.LOBBY - onClickFunction = { joinGame(lobby.id) } - } - } + bpButton( + minimal = true, + title = "Join Game", + icon = "arrow-right", + disabled = lobby.state != State.LOBBY, + onClick = { joinGame(lobby.id) } + ) } val gameList = connect( diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/home/ChooseNameForm.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/home/ChooseNameForm.kt index 91f6e291..b0a7b5e2 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/home/ChooseNameForm.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/home/ChooseNameForm.kt @@ -2,7 +2,7 @@ package org.luxons.sevenwonders.ui.components.home import com.palantir.blueprintjs.Intent import com.palantir.blueprintjs.bpButton -import com.palantir.blueprintjs.inputGroup +import com.palantir.blueprintjs.bpInputGroup import com.palantir.blueprintjs.org.luxons.sevenwonders.ui.utils.createElement import kotlinx.html.js.onSubmitFunction import org.luxons.sevenwonders.ui.redux.RequestChooseName @@ -32,7 +32,7 @@ private class ChooseNameForm(props: ChooseNameFormProps): RComponent chooseUsername(e) } - inputGroup( + bpInputGroup( large = true, placeholder = "Username", rightElement = submitButton(), diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt index f096c5db..7c6d688f 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/RadialPlayerList.kt @@ -2,7 +2,7 @@ package org.luxons.sevenwonders.ui.components.lobby import com.palantir.blueprintjs.IconName import com.palantir.blueprintjs.Intent -import com.palantir.blueprintjs.icon +import com.palantir.blueprintjs.bpIcon import kotlinx.css.Align import kotlinx.css.Display import kotlinx.css.FlexDirection @@ -104,5 +104,5 @@ private fun RBuilder.playerPlaceholder(): ReactElement = styledDiv { private fun RBuilder.userIcon(isUser: Boolean, isOwner: Boolean, title: String?): ReactElement { val iconName: IconName = if (isOwner) "badge" else "user" val intent: Intent = if (isUser) Intent.WARNING else Intent.NONE - return icon(name = iconName, intent = intent, size = 50, title = title) + return bpIcon(name = iconName, intent = intent, size = 50, title = title) } -- cgit