diff options
author | Joffrey Bion <joffrey.bion@booking.com> | 2020-03-22 23:06:19 +0100 |
---|---|---|
committer | Joffrey Bion <joffrey.bion@booking.com> | 2020-03-22 23:16:59 +0100 |
commit | 7730a8ad565c167029681fd15d98be6c7dce0492 (patch) | |
tree | 9f0edfc7174298d36d717586aaf35ef961d1f0f5 /sw-ui-kt | |
parent | Fix home refresh on enter key (diff) | |
download | seven-wonders-7730a8ad565c167029681fd15d98be6c7dce0492.tar.gz seven-wonders-7730a8ad565c167029681fd15d98be6c7dce0492.tar.bz2 seven-wonders-7730a8ad565c167029681fd15d98be6c7dce0492.zip |
Migrate game browser to blueprint components
Diffstat (limited to 'sw-ui-kt')
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<IIconProps, RState> { } 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<IInputGroupProps, IInputGroupState> { 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<ITagProps, RState> { + 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<IIconProps> = {} ): 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<IButtonProps> = {} ): 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<ITagProps> = {} +): 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<CreateGameF } override fun RBuilder.render() { - form { - attrs.onSubmitFunction = { props.createGame(state.gameName) } - - input(type = InputType.text) { + styledDiv { + css { + display = Display.flex + flexDirection = FlexDirection.row + justifyContent = JustifyContent.spaceBetween + } + form { attrs { - value = state.gameName - onChangeFunction = { e -> + 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<RProps> = 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<LobbyDTO> @@ -30,6 +43,9 @@ class GameListPresenter(props: GameListProps) : RComponent<GameListProps, RState override fun RBuilder.render() { table { + attrs { + classes = setOf(Classes.HTML_TABLE) + } thead { gameListHeaderRow() } @@ -49,58 +65,54 @@ private fun RBuilder.gameListHeaderRow() = tr { th { +"Join" } } -private fun RBuilder.gameListItemRow(lobby: LobbyDTO, joinGame: (Long) -> 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 - // <Tag minimal intent={statusIntents[state]}>{state}</Tag> - // 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) { - //<div title='Number of players'> - // <Icon className="playerCountIcon" icon="people" title={false} /> - // <span className="playerCount"> {nbPlayers}</span> - //</div>; - - // 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<GameListStateProps, GameListDispatchProps, GameListProps>( 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<ChooseNameF override fun RBuilder.render() { form { attrs.onSubmitFunction = { e -> 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) } |