mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 22:58:29 +08:00
Made widget styles more reusable and customizable.
This commit is contained in:
parent
36a32018ca
commit
737a44303d
@ -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> =
|
||||
|
@ -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) =
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
"""
|
||||
|
@ -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;
|
||||
}
|
||||
"""
|
||||
|
@ -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);
|
||||
}
|
||||
"""
|
||||
|
@ -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);
|
||||
}
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
"""
|
@ -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)
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
"""
|
||||
|
@ -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;
|
||||
}
|
||||
"""
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user