Made widget styles more reusable and customizable.

This commit is contained in:
Daan Vanden Bosch 2020-10-11 11:53:17 +02:00
parent 36a32018ca
commit 737a44303d
21 changed files with 217 additions and 120 deletions

View File

@ -53,8 +53,8 @@ class PwResultBuilder<T>(private val logger: KLogger) {
Severity.Error -> logger.error(cause) { message ?: uiMessage }
}
problems.add(Problem(severity, uiMessage));
return this;
problems.add(Problem(severity, uiMessage))
return this
}
/**
@ -62,7 +62,7 @@ class PwResultBuilder<T>(private val logger: KLogger) {
*/
fun addResult(result: PwResult<*>): PwResultBuilder<T> {
problems.addAll(result.problems)
return this;
return this
}
fun success(value: T): Success<T> =

View File

@ -11,11 +11,11 @@ abstract class DisposableContainer : TrackedDisposable() {
}
protected fun <T : Disposable> addDisposable(disposable: T): T {
return disposer.add(disposable);
return disposer.add(disposable)
}
protected fun addDisposables(vararg disposables: Disposable) {
disposer.addAll(*disposables);
disposer.addAll(*disposables)
}
protected fun removeDisposable(disposable: Disposable) =

View File

@ -2,7 +2,6 @@ package world.phantasmal.observable.value.list
import world.phantasmal.core.disposable.Disposable
import world.phantasmal.observable.value.Val
import kotlin.reflect.KProperty
interface ListVal<E> : Val<List<E>>, List<E> {
val sizeVal: Val<Int>

View File

@ -24,7 +24,7 @@ private fun init(): Disposable {
val rootNode = document.body!!
disposer.add(Application(rootNode, HistoryApplicationUrl()))
disposer.add(Application(rootNode, disposer.add(HistoryApplicationUrl())))
return disposer
}

View File

@ -1,7 +1,7 @@
package world.phantasmal.web.application.widgets
import org.w3c.dom.Node
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.dom.root
import world.phantasmal.webui.widgets.Widget
class ApplicationWidget(
@ -9,7 +9,7 @@ class ApplicationWidget(
private val mainContentWidget: MainContentWidget,
) : Widget(::style) {
override fun Node.createElement() =
div(className = "pw-application-application") {
root(className = "pw-application-application") {
addChild(navigationWidget)
addChild(mainContentWidget)
}
@ -24,5 +24,10 @@ private fun style() = """
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
}
.pw-application-application .pw-application-main-content {
flex: 1;
}
"""

View File

@ -11,7 +11,7 @@ import world.phantasmal.webui.widgets.Widget
class MainContentWidget(
private val ctrl: MainContentController,
private val toolViews: Map<PwTool, () -> Widget>,
) : Widget() {
) : Widget(::style) {
override fun Node.createElement() = div(className = "pw-application-main-content") {
ctrl.tools.forEach { (tool, active) ->
toolViews[tool]?.let { createWidget ->
@ -20,3 +20,16 @@ class MainContentWidget(
}
}
}
@Suppress("CssUnusedSymbol")
// language=css
private fun style() = """
.pw-application-main-content {
display: flex;
flex-direction: column;
}
.pw-application-main-content > * {
flex: 1;
}
"""

View File

@ -14,7 +14,7 @@ class NavigationWidget(private val ctrl: NavigationController) : Widget(::style)
}
}
@Suppress("CssUnusedSymbol")
@Suppress("CssUnusedSymbol", "CssUnresolvedCustomProperty")
// language=css
private fun style() = """
.pw-application-navigation {
@ -23,7 +23,7 @@ private fun style() = """
flex-direction: row;
align-items: stretch;
background-color: hsl(0, 0%, 10%);
border-bottom: solid 2px var(--bg-color);
border-bottom: solid 2px var(--pw-bg-color);
}
.pw-application-navigation-spacer {
@ -51,10 +51,10 @@ private fun style() = """
justify-content: center;
width: 30px;
font-size: 16px;
color: var(--control-text-color);
color: var(--pw-control-text-color);
}
.pw-application-navigation-github:hover {
color: var(--control-text-color-hover);
color: var(--pw-control-text-color-hover);
}
"""

View File

@ -28,6 +28,7 @@ class PwToolButton(
}
}
@Suppress("CssUnresolvedCustomProperty")
// language=css
private fun style() = """
.pw-application-pw-tool-button input {
@ -52,6 +53,6 @@ private fun style() = """
.pw-application-pw-tool-button input:checked + label {
color: hsl(0, 0%, 85%);
background-color: var(--bg-color);
background-color: var(--pw-bg-color);
}
"""

View File

@ -2,6 +2,8 @@ package world.phantasmal.web.core.controllers
import world.phantasmal.web.core.stores.PwTool
import world.phantasmal.web.core.stores.UiStore
import world.phantasmal.webui.controllers.Tab
import world.phantasmal.webui.controllers.TabController
open class PathAwareTab(override val title: String, val path: String) : Tab

View File

@ -25,8 +25,14 @@ class HelpWidget : Widget(::style) {
}
}
@Suppress("CssUnusedSymbol")
// language=css
private fun style() = """
.pw-huntoptimizer-help {
cursor: initial;
user-select: text;
}
.pw-huntoptimizer-help p {
margin: 1em;
max-width: 600px;

View File

@ -1,7 +1,7 @@
package world.phantasmal.web.huntoptimizer.widgets
import org.w3c.dom.Node
import world.phantasmal.web.core.widgets.TabContainer
import world.phantasmal.webui.widgets.TabContainer
import world.phantasmal.web.huntoptimizer.HuntOptimizerUrls
import world.phantasmal.web.huntoptimizer.controllers.HuntOptimizerController
import world.phantasmal.webui.dom.div
@ -10,7 +10,7 @@ import world.phantasmal.webui.widgets.Widget
class HuntOptimizerWidget(
private val ctrl: HuntOptimizerController,
private val createMethodsWidget: () -> MethodsWidget,
) : Widget() {
) : Widget(::style) {
override fun Node.createElement() = div(className = "pw-huntoptimizer-hunt-optimizer") {
addChild(TabContainer(
ctrl = ctrl,
@ -29,3 +29,16 @@ class HuntOptimizerWidget(
))
}
}
@Suppress("CssUnusedSymbol")
// language=css
private fun style() = """
.pw-huntoptimizer-hunt-optimizer {
display: flex;
flex-direction: column;
}
.pw-huntoptimizer-hunt-optimizer > * {
flex: 1;
}
"""

View File

@ -1,7 +1,7 @@
package world.phantasmal.web.huntoptimizer.widgets
import org.w3c.dom.Node
import world.phantasmal.web.core.widgets.TabContainer
import world.phantasmal.webui.widgets.TabContainer
import world.phantasmal.web.huntoptimizer.controllers.MethodsController
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.widgets.Widget

View File

@ -1,83 +0,0 @@
/* Defaults */
:root {
/* Basic Widget variables */
--bg-color: hsl(0, 0%, 15%);
--text-color: hsl(0, 0%, 80%);
--text-color-disabled: hsl(0, 0%, 55%);
--font-family: Verdana, Geneva, sans-serif;
--border-color: hsl(0, 0%, 25%);
--border-color-focus: hsl(0, 0%, 35%);
--border: solid 1px var(--border-color);
--border-focus: solid 1px var(--border-color-focus);
/* Scrollbars */
--scrollbar-color: hsl(0, 0%, 13%);
--scrollbar-thumb-color: hsl(0, 0%, 17%);
/* Controls */
--control-bg-color: hsl(0, 0%, 20%);
--control-bg-color-hover: hsl(0, 0%, 25%);
--control-text-color: hsl(0, 0%, 80%);
--control-text-color-hover: hsl(0, 0%, 90%);
--control-border: solid 1px hsl(0, 0%, 10%);
--control-inner-border: solid 1px hsl(0, 0%, 35%);
--control-inner-border-focus: solid 1px hsl(0, 0%, 50%);
/* Inputs */
--input-bg-color: hsl(0, 0%, 12%);
--input-bg-color-disabled: hsl(0, 0%, 15%);
--input-text-color: hsl(0, 0%, 75%);
--input-text-color-disabled: var(--text-color-disabled);
--input-border: solid 1px hsl(0, 0%, 25%);
--input-border-hover: solid 1px hsl(0, 0%, 30%);
--input-border-focus: solid 1px hsl(0, 0%, 40%);
--input-border-disabled: solid 1px hsl(0, 0%, 20%);
--input-inner-border: solid 1px hsl(0, 0%, 5%);
}
* {
scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-color);
}
::-webkit-scrollbar {
background-color: var(--scrollbar-color);
}
::-webkit-scrollbar-track {
background-color: var(--scrollbar-color);
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb-color);
}
::-webkit-scrollbar-corner {
background-color: var(--scrollbar-color);
}
body {
cursor: default;
user-select: none;
overflow: hidden;
margin: 0;
font-size: 13px;
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-family);
}
h2 {
font-size: 1.1em;
margin: 0.5em 0;
}
#root *[hidden] {
display: none;
}

View File

@ -5,9 +5,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Phantasmal World</title>
<script src="web.js" async></script>
<link rel="stylesheet" href="index.css">
<style>
body {
overflow: hidden;
margin: 0;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,9 +1,8 @@
package world.phantasmal.web.core.controllers
package world.phantasmal.webui.controllers
import world.phantasmal.observable.value.MutableVal
import world.phantasmal.observable.value.Val
import world.phantasmal.observable.value.mutableVal
import world.phantasmal.webui.controllers.Controller
interface Tab {
val title: String

View File

@ -0,0 +1,95 @@
package world.phantasmal.webui.dom
@Suppress("CssUnusedSymbol", "CssUnresolvedCustomProperty")
// language=css
internal const val DEFAULT_STYLE = """
#pw-root {
/* Basic Widget variables */
--pw-bg-color: hsl(0, 0%, 15%);
--pw-text-color: hsl(0, 0%, 80%);
--pw-text-color-disabled: hsl(0, 0%, 55%);
--pw-font-family: Verdana, Geneva, sans-serif;
--pw-border-color: hsl(0, 0%, 25%);
--pw-border-color-focus: hsl(0, 0%, 35%);
--pw-border: solid 1px var(--pw-border-color);
--pw-border-focus: solid 1px var(--pw-border-color-focus);
/* Scrollbars */
--pw-scrollbar-color: hsl(0, 0%, 13%);
--pw-scrollbar-thumb-color: hsl(0, 0%, 17%);
/* Controls */
--pw-control-bg-color: hsl(0, 0%, 20%);
--pw-control-bg-color-hover: hsl(0, 0%, 25%);
--pw-control-text-color: hsl(0, 0%, 80%);
--pw-control-text-color-hover: hsl(0, 0%, 90%);
--pw-control-border: solid 1px hsl(0, 0%, 10%);
--pw-control-inner-border: solid 1px hsl(0, 0%, 35%);
--pw-control-inner-border-focus: solid 1px hsl(0, 0%, 50%);
/* Inputs */
--pw-input-bg-color: hsl(0, 0%, 12%);
--pw-input-bg-color-disabled: hsl(0, 0%, 15%);
--pw-input-text-color: hsl(0, 0%, 75%);
--pw-input-text-color-disabled: var(--pw-text-color-disabled);
--pw-input-border: solid 1px hsl(0, 0%, 25%);
--pw-input-border-hover: solid 1px hsl(0, 0%, 30%);
--pw-input-border-focus: solid 1px hsl(0, 0%, 40%);
--pw-input-border-disabled: solid 1px hsl(0, 0%, 20%);
--pw-input-inner-border: solid 1px hsl(0, 0%, 5%);
/* TabContainer */
--pw-tab-bg-color: hsl(0, 0%, 12%);
--pw-tab-bg-color-hover: hsl(0, 0%, 18%);
--pw-tab-bg-color-active: var(--pw-bg-color);
--pw-tab-text-color: hsl(0, 0%, 75%);
--pw-tab-text-color-hover: hsl(0, 0%, 85%);
--pw-tab-text-color-active: hsl(0, 0%, 90%);
/* Root element styling */
cursor: default;
user-select: none;
overflow: hidden;
font-size: 13px;
background-color: var(--pw-bg-color);
color: var(--pw-text-color);
font-family: var(--pw-font-family), sans-serif;
}
#pw-root * {
scrollbar-color: var(--pw-scrollbar-thumb-color) var(--pw-scrollbar-color);
}
#pw-root ::-webkit-scrollbar {
background-color: var(--pw-scrollbar-color);
}
#pw-root ::-webkit-scrollbar-track {
background-color: var(--pw-scrollbar-color);
}
#pw-root ::-webkit-scrollbar-thumb {
background-color: var(--pw-scrollbar-thumb-color);
}
#pw-root ::-webkit-scrollbar-corner {
background-color: var(--pw-scrollbar-color);
}
#pw-root h2 {
font-size: 1.1em;
margin: 0.5em 0;
}
#pw-root *[hidden] {
display: none;
}
"""

View File

@ -1,6 +1,11 @@
package world.phantasmal.webui.dom
import kotlinx.browser.document
import kotlinx.dom.appendText
import org.w3c.dom.AddEventListenerOptions
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLStyleElement
import org.w3c.dom.Node
import org.w3c.dom.events.Event
import org.w3c.dom.events.EventTarget
import world.phantasmal.core.disposable.Disposable
@ -19,3 +24,12 @@ fun <E : Event> disposableListener(
target.removeEventListener(type, listener)
}
}
fun Node.root(className: String? = null, block: HTMLElement.() -> Unit): HTMLElement {
val styleEl = document.createElement("style") as HTMLStyleElement
styleEl.id = "pw-root-styles"
styleEl.appendText(DEFAULT_STYLE)
document.head!!.append(styleEl)
return div(id = "pw-root", className, block = block)
}

View File

@ -8,7 +8,7 @@ import world.phantasmal.webui.dom.div
class LazyLoader(
hidden: Val<Boolean> = falseVal(),
private val createWidget: () -> Widget,
) : Widget(NO_STYLE, hidden) {
) : Widget(::style, hidden) {
private var initialized = false
override fun Node.createElement() = div(className = "pw-lazy-loader") {
@ -20,3 +20,16 @@ class LazyLoader(
}
}
}
@Suppress("CssUnusedSymbol")
// language=css
private fun style() = """
.pw-lazy-loader {
display: flex;
flex-direction: column;
}
.pw-lazy-loader > * {
flex: 1;
}
"""

View File

@ -1,14 +1,12 @@
package world.phantasmal.web.core.widgets
package world.phantasmal.webui.widgets
import org.w3c.dom.Node
import world.phantasmal.observable.value.Val
import world.phantasmal.observable.value.falseVal
import world.phantasmal.web.core.controllers.Tab
import world.phantasmal.web.core.controllers.TabController
import world.phantasmal.webui.controllers.Tab
import world.phantasmal.webui.controllers.TabController
import world.phantasmal.webui.dom.div
import world.phantasmal.webui.dom.span
import world.phantasmal.webui.widgets.LazyLoader
import world.phantasmal.webui.widgets.Widget
class TabContainer<T : Tab>(
hidden: Val<Boolean> = falseVal(),
@ -56,11 +54,16 @@ class TabContainer<T : Tab>(
@Suppress("CssUnresolvedCustomProperty", "CssUnusedSymbol")
// language=css
private fun style() = """
.pw-tab-container {
display: flex;
flex-direction: column;
}
.pw-tab-container-bar {
box-sizing: border-box;
height: 28px;
padding: 3px 3px 0 3px;
border-bottom: var(--border);
border-bottom: var(--pw-border);
}
.pw-tab-container-tab {
@ -69,21 +72,31 @@ private fun style() = """
align-items: center;
height: calc(100% + 1px);
padding: 0 10px;
border: var(--border);
border: var(--pw-border);
margin: 0 1px -1px 1px;
background-color: hsl(0, 0%, 12%);
color: hsl(0, 0%, 75%);
background-color: var(--pw-tab-bg-color);
color: var(--pw-tab-text-color);
font-size: 13px;
}
.pw-tab-container-tab:hover {
background-color: hsl(0, 0%, 18%);
color: hsl(0, 0%, 85%);
background-color: var(--pw-tab-bg-color-hover);
color: var(--pw-tab-text-color-hover);
}
.pw-tab-container-tab.active {
background-color: var(--bg-color);
color: hsl(0, 0%, 90%);
border-bottom-color: var(--bg-color);
background-color: var(--pw-tab-bg-color-active);
color: var(--pw-tab-text-color-active);
border-bottom-color: var(--pw-tab-bg-color-active);
}
.pw-tab-container-panes {
flex: 1;
display: flex;
flex-direction: row;
}
.pw-tab-container-panes > * {
flex: 1;
}
"""

View File

@ -22,6 +22,8 @@ abstract class Widget(
private val _children = mutableListOf<Widget>()
private val elementDelegate = lazy {
// Add CSS declarations to stylesheet if this is the first time we're instantiating this
// widget.
if (style !== NO_STYLE && STYLES_ADDED.add(this::class)) {
STYLE_EL.appendText(style())
}
@ -157,6 +159,7 @@ abstract class Widget(
companion object {
private val STYLE_EL by lazy {
val el = document.createElement("style") as HTMLStyleElement
el.id = "pw-widget-styles"
document.head!!.append(el)
el
}