mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 22:58:29 +08:00
Added hunt method tables with dummy data.
This commit is contained in:
parent
540e35ffc9
commit
41f8e53efc
@ -6,8 +6,10 @@ import world.phantasmal.web.core.loading.AssetLoader
|
|||||||
import world.phantasmal.web.core.stores.UiStore
|
import world.phantasmal.web.core.stores.UiStore
|
||||||
import world.phantasmal.web.huntOptimizer.controllers.HuntOptimizerController
|
import world.phantasmal.web.huntOptimizer.controllers.HuntOptimizerController
|
||||||
import world.phantasmal.web.huntOptimizer.controllers.MethodsController
|
import world.phantasmal.web.huntOptimizer.controllers.MethodsController
|
||||||
|
import world.phantasmal.web.huntOptimizer.controllers.MethodsForEpisodeController
|
||||||
import world.phantasmal.web.huntOptimizer.stores.HuntMethodStore
|
import world.phantasmal.web.huntOptimizer.stores.HuntMethodStore
|
||||||
import world.phantasmal.web.huntOptimizer.widgets.HuntOptimizerWidget
|
import world.phantasmal.web.huntOptimizer.widgets.HuntOptimizerWidget
|
||||||
|
import world.phantasmal.web.huntOptimizer.widgets.MethodsForEpisodeWidget
|
||||||
import world.phantasmal.web.huntOptimizer.widgets.MethodsWidget
|
import world.phantasmal.web.huntOptimizer.widgets.MethodsWidget
|
||||||
import world.phantasmal.webui.DisposableContainer
|
import world.phantasmal.webui.DisposableContainer
|
||||||
import world.phantasmal.webui.widgets.Widget
|
import world.phantasmal.webui.widgets.Widget
|
||||||
@ -24,12 +26,16 @@ class HuntOptimizer(
|
|||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
val huntOptimizerController = addDisposable(HuntOptimizerController(uiStore))
|
val huntOptimizerController = addDisposable(HuntOptimizerController(uiStore))
|
||||||
val methodsController = addDisposable(MethodsController(uiStore, huntMethodStore))
|
val methodsController = addDisposable(MethodsController(uiStore))
|
||||||
|
|
||||||
// Main Widget
|
// Main Widget
|
||||||
return HuntOptimizerWidget(
|
return HuntOptimizerWidget(
|
||||||
ctrl = huntOptimizerController,
|
ctrl = huntOptimizerController,
|
||||||
createMethodsWidget = { MethodsWidget(methodsController) }
|
createMethodsWidget = {
|
||||||
|
MethodsWidget(methodsController) { episode ->
|
||||||
|
MethodsForEpisodeWidget(MethodsForEpisodeController(huntMethodStore, episode))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,15 @@
|
|||||||
package world.phantasmal.web.huntOptimizer.controllers
|
package world.phantasmal.web.huntOptimizer.controllers
|
||||||
|
|
||||||
import world.phantasmal.lib.fileFormats.quest.Episode
|
import world.phantasmal.lib.fileFormats.quest.Episode
|
||||||
import world.phantasmal.observable.value.list.ListVal
|
|
||||||
import world.phantasmal.observable.value.list.MutableListVal
|
|
||||||
import world.phantasmal.observable.value.list.mutableListVal
|
|
||||||
import world.phantasmal.web.core.PwToolType
|
import world.phantasmal.web.core.PwToolType
|
||||||
import world.phantasmal.web.core.controllers.PathAwareTab
|
import world.phantasmal.web.core.controllers.PathAwareTab
|
||||||
import world.phantasmal.web.core.controllers.PathAwareTabController
|
import world.phantasmal.web.core.controllers.PathAwareTabController
|
||||||
import world.phantasmal.web.core.stores.UiStore
|
import world.phantasmal.web.core.stores.UiStore
|
||||||
import world.phantasmal.web.huntOptimizer.HuntOptimizerUrls
|
import world.phantasmal.web.huntOptimizer.HuntOptimizerUrls
|
||||||
import world.phantasmal.web.huntOptimizer.models.HuntMethodModel
|
|
||||||
import world.phantasmal.web.huntOptimizer.stores.HuntMethodStore
|
|
||||||
|
|
||||||
class MethodsTab(title: String, path: String, val episode: Episode) : PathAwareTab(title, path)
|
class MethodsTab(title: String, path: String, val episode: Episode) : PathAwareTab(title, path)
|
||||||
|
|
||||||
class MethodsController(
|
class MethodsController(uiStore: UiStore) : PathAwareTabController<MethodsTab>(
|
||||||
uiStore: UiStore,
|
|
||||||
huntMethodStore: HuntMethodStore,
|
|
||||||
) : PathAwareTabController<MethodsTab>(
|
|
||||||
uiStore,
|
uiStore,
|
||||||
PwToolType.HuntOptimizer,
|
PwToolType.HuntOptimizer,
|
||||||
listOf(
|
listOf(
|
||||||
@ -25,29 +17,4 @@ class MethodsController(
|
|||||||
MethodsTab("Episode II", HuntOptimizerUrls.methodsEpisodeII, Episode.II),
|
MethodsTab("Episode II", HuntOptimizerUrls.methodsEpisodeII, Episode.II),
|
||||||
MethodsTab("Episode IV", HuntOptimizerUrls.methodsEpisodeIV, Episode.IV),
|
MethodsTab("Episode IV", HuntOptimizerUrls.methodsEpisodeIV, Episode.IV),
|
||||||
)
|
)
|
||||||
) {
|
)
|
||||||
private val _episodeToMethods = mutableMapOf<Episode, MutableListVal<HuntMethodModel>>()
|
|
||||||
|
|
||||||
val episodeToMethods: Map<Episode, ListVal<HuntMethodModel>> = _episodeToMethods
|
|
||||||
|
|
||||||
init {
|
|
||||||
// TODO: Use filtered ListVals.
|
|
||||||
observe(huntMethodStore.methods) { methods ->
|
|
||||||
val ep1 = _episodeToMethods.getOrPut(Episode.I) { mutableListVal() }
|
|
||||||
val ep2 = _episodeToMethods.getOrPut(Episode.II) { mutableListVal() }
|
|
||||||
val ep4 = _episodeToMethods.getOrPut(Episode.IV) { mutableListVal() }
|
|
||||||
|
|
||||||
ep1.clear()
|
|
||||||
ep2.clear()
|
|
||||||
ep4.clear()
|
|
||||||
|
|
||||||
methods.forEach { method ->
|
|
||||||
when (method.episode) {
|
|
||||||
Episode.I -> ep1.add(method)
|
|
||||||
Episode.II -> ep2.add(method)
|
|
||||||
Episode.IV -> ep4.add(method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package world.phantasmal.web.huntOptimizer.controllers
|
||||||
|
|
||||||
|
import world.phantasmal.lib.fileFormats.quest.Episode
|
||||||
|
import world.phantasmal.lib.fileFormats.quest.NpcType
|
||||||
|
import world.phantasmal.observable.value.list.ListVal
|
||||||
|
import world.phantasmal.web.huntOptimizer.models.HuntMethodModel
|
||||||
|
import world.phantasmal.web.huntOptimizer.stores.HuntMethodStore
|
||||||
|
import world.phantasmal.webui.controllers.Controller
|
||||||
|
|
||||||
|
class MethodsForEpisodeController(
|
||||||
|
huntMethodStore: HuntMethodStore,
|
||||||
|
episode: Episode,
|
||||||
|
) : Controller() {
|
||||||
|
val enemies: List<NpcType> = NpcType.VALUES.filter { it.enemy && it.episode == episode }
|
||||||
|
|
||||||
|
val methods: ListVal<HuntMethodModel> =
|
||||||
|
huntMethodStore.methods.filtered { it.episode == episode }
|
||||||
|
}
|
@ -1,22 +1,44 @@
|
|||||||
package world.phantasmal.web.huntOptimizer.widgets
|
package world.phantasmal.web.huntOptimizer.widgets
|
||||||
|
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import world.phantasmal.lib.fileFormats.quest.Episode
|
import world.phantasmal.web.huntOptimizer.controllers.MethodsForEpisodeController
|
||||||
import world.phantasmal.web.huntOptimizer.controllers.MethodsController
|
import world.phantasmal.web.huntOptimizer.models.HuntMethodModel
|
||||||
import world.phantasmal.webui.dom.div
|
import world.phantasmal.webui.dom.div
|
||||||
|
import world.phantasmal.webui.widgets.Column
|
||||||
|
import world.phantasmal.webui.widgets.Table
|
||||||
import world.phantasmal.webui.widgets.Widget
|
import world.phantasmal.webui.widgets.Widget
|
||||||
|
|
||||||
class MethodsForEpisodeWidget(
|
class MethodsForEpisodeWidget(private val ctrl: MethodsForEpisodeController) : Widget() {
|
||||||
private val ctrl: MethodsController,
|
|
||||||
private val episode: Episode,
|
|
||||||
) : Widget() {
|
|
||||||
override fun Node.createElement() =
|
override fun Node.createElement() =
|
||||||
div {
|
div {
|
||||||
className = "pw-hunt-optimizer-methods-for-episode"
|
className = "pw-hunt-optimizer-methods-for-episode"
|
||||||
|
|
||||||
bindChildrenTo(ctrl.episodeToMethods.getValue(episode)) { method, _ ->
|
addChild(
|
||||||
div { textContent = method.name }
|
Table(
|
||||||
}
|
values = ctrl.methods,
|
||||||
|
columns = listOf(
|
||||||
|
Column(
|
||||||
|
title = "Method",
|
||||||
|
fixed = true,
|
||||||
|
width = 250,
|
||||||
|
renderCell = { it.name },
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
title = "Time",
|
||||||
|
fixed = true,
|
||||||
|
width = 60,
|
||||||
|
renderCell = { it.time.value.toIsoString() },
|
||||||
|
),
|
||||||
|
*ctrl.enemies.map { enemy ->
|
||||||
|
Column<HuntMethodModel>(
|
||||||
|
title = enemy.simpleName,
|
||||||
|
width = 90,
|
||||||
|
renderCell = { 69 }
|
||||||
|
)
|
||||||
|
}.toTypedArray()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -25,7 +47,10 @@ class MethodsForEpisodeWidget(
|
|||||||
// language=css
|
// language=css
|
||||||
style("""
|
style("""
|
||||||
.pw-hunt-optimizer-methods-for-episode {
|
.pw-hunt-optimizer-methods-for-episode {
|
||||||
overflow: auto;
|
display: grid;
|
||||||
|
grid-template-rows: 100%;
|
||||||
|
grid-template-columns: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package world.phantasmal.web.huntOptimizer.widgets
|
package world.phantasmal.web.huntOptimizer.widgets
|
||||||
|
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
|
import world.phantasmal.lib.fileFormats.quest.Episode
|
||||||
import world.phantasmal.web.huntOptimizer.controllers.MethodsController
|
import world.phantasmal.web.huntOptimizer.controllers.MethodsController
|
||||||
import world.phantasmal.webui.dom.div
|
import world.phantasmal.webui.dom.div
|
||||||
import world.phantasmal.webui.widgets.TabContainer
|
import world.phantasmal.webui.widgets.TabContainer
|
||||||
@ -8,13 +9,14 @@ import world.phantasmal.webui.widgets.Widget
|
|||||||
|
|
||||||
class MethodsWidget(
|
class MethodsWidget(
|
||||||
private val ctrl: MethodsController,
|
private val ctrl: MethodsController,
|
||||||
|
private val createMethodsForEpisodeWidget: (Episode) -> MethodsForEpisodeWidget,
|
||||||
) : Widget() {
|
) : Widget() {
|
||||||
override fun Node.createElement() =
|
override fun Node.createElement() =
|
||||||
div {
|
div {
|
||||||
className = "pw-hunt-optimizer-methods"
|
className = "pw-hunt-optimizer-methods"
|
||||||
|
|
||||||
addChild(TabContainer(ctrl = ctrl, createWidget = { tab ->
|
addChild(TabContainer(ctrl = ctrl, createWidget = { tab ->
|
||||||
MethodsForEpisodeWidget(ctrl, tab.episode)
|
createMethodsForEpisodeWidget(tab.episode)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,12 +26,9 @@ class MethodsWidget(
|
|||||||
// language=css
|
// language=css
|
||||||
style("""
|
style("""
|
||||||
.pw-hunt-optimizer-methods {
|
.pw-hunt-optimizer-methods {
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-rows: 100%;
|
||||||
}
|
grid-template-columns: 100%;
|
||||||
|
|
||||||
.pw-hunt-optimizer-methods > * {
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
|
@ -62,7 +62,7 @@ object AsmAnalyser {
|
|||||||
processAsm()
|
processAsm()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateAssembly(changes: List<AsmChange>) {
|
suspend fun updateAsm(changes: List<AsmChange>) {
|
||||||
for (change in changes) {
|
for (change in changes) {
|
||||||
val (startLineNo, startCol, endLineNo, endCol) = change.range
|
val (startLineNo, startCol, endLineNo, endCol) = change.range
|
||||||
val linesChanged = endLineNo - startLineNo + 1
|
val linesChanged = endLineNo - startLineNo + 1
|
||||||
|
@ -66,7 +66,7 @@ class AsmStore(
|
|||||||
|
|
||||||
model.onDidChangeContent { e ->
|
model.onDidChangeContent { e ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
AsmAnalyser.updateAssembly(e.changes.map {
|
AsmAnalyser.updateAsm(e.changes.map {
|
||||||
AsmChange(
|
AsmChange(
|
||||||
AsmRange(
|
AsmRange(
|
||||||
it.range.startLineNumber,
|
it.range.startLineNumber,
|
||||||
|
@ -57,15 +57,24 @@ fun Node.span(block: HTMLSpanElement.() -> Unit = {}): HTMLSpanElement =
|
|||||||
fun Node.table(block: HTMLTableElement.() -> Unit = {}): HTMLTableElement =
|
fun Node.table(block: HTMLTableElement.() -> Unit = {}): HTMLTableElement =
|
||||||
appendHtmlEl("TABLE", block)
|
appendHtmlEl("TABLE", block)
|
||||||
|
|
||||||
|
fun Node.tbody(block: HTMLTableSectionElement.() -> Unit = {}): HTMLTableSectionElement =
|
||||||
|
appendHtmlEl("TBODY", block)
|
||||||
|
|
||||||
fun Node.td(block: HTMLTableCellElement.() -> Unit = {}): HTMLTableCellElement =
|
fun Node.td(block: HTMLTableCellElement.() -> Unit = {}): HTMLTableCellElement =
|
||||||
appendHtmlEl("TD", block)
|
appendHtmlEl("TD", block)
|
||||||
|
|
||||||
fun Node.textarea(block: HTMLTextAreaElement.() -> Unit = {}): HTMLTextAreaElement =
|
fun Node.textarea(block: HTMLTextAreaElement.() -> Unit = {}): HTMLTextAreaElement =
|
||||||
appendHtmlEl("TEXTAREA", block)
|
appendHtmlEl("TEXTAREA", block)
|
||||||
|
|
||||||
|
fun Node.tfoot(block: HTMLTableSectionElement.() -> Unit = {}): HTMLTableSectionElement =
|
||||||
|
appendHtmlEl("TFOOT", block)
|
||||||
|
|
||||||
fun Node.th(block: HTMLTableCellElement.() -> Unit = {}): HTMLTableCellElement =
|
fun Node.th(block: HTMLTableCellElement.() -> Unit = {}): HTMLTableCellElement =
|
||||||
appendHtmlEl("TH", block)
|
appendHtmlEl("TH", block)
|
||||||
|
|
||||||
|
fun Node.thead(block: HTMLTableSectionElement.() -> Unit = {}): HTMLTableSectionElement =
|
||||||
|
appendHtmlEl("THEAD", block)
|
||||||
|
|
||||||
fun Node.tr(block: HTMLTableRowElement.() -> Unit = {}): HTMLTableRowElement =
|
fun Node.tr(block: HTMLTableRowElement.() -> Unit = {}): HTMLTableRowElement =
|
||||||
appendHtmlEl("TR", block)
|
appendHtmlEl("TR", block)
|
||||||
|
|
||||||
|
168
webui/src/main/kotlin/world/phantasmal/webui/widgets/Table.kt
Normal file
168
webui/src/main/kotlin/world/phantasmal/webui/widgets/Table.kt
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package world.phantasmal.webui.widgets
|
||||||
|
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
import world.phantasmal.observable.value.Val
|
||||||
|
import world.phantasmal.observable.value.list.ListVal
|
||||||
|
import world.phantasmal.observable.value.trueVal
|
||||||
|
import world.phantasmal.webui.dom.*
|
||||||
|
|
||||||
|
class Column<T>(
|
||||||
|
val title: String,
|
||||||
|
val fixed: Boolean = false,
|
||||||
|
val width: Int,
|
||||||
|
val renderCell: (T) -> Any,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Table<T>(
|
||||||
|
visible: Val<Boolean> = trueVal(),
|
||||||
|
enabled: Val<Boolean> = trueVal(),
|
||||||
|
private val values: ListVal<T>,
|
||||||
|
private val columns: List<Column<T>>,
|
||||||
|
) : Widget(visible, enabled) {
|
||||||
|
override fun Node.createElement() =
|
||||||
|
table {
|
||||||
|
className = "pw-table"
|
||||||
|
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
var runningWidth = 0
|
||||||
|
|
||||||
|
for ((index, column) in columns.withIndex()) {
|
||||||
|
th {
|
||||||
|
span { textContent = column.title }
|
||||||
|
|
||||||
|
if (column.fixed) {
|
||||||
|
style.position = "sticky"
|
||||||
|
style.left = "${runningWidth}px"
|
||||||
|
runningWidth += column.width
|
||||||
|
}
|
||||||
|
|
||||||
|
style.width = "${column.width}px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
bindChildrenTo(values) { value, index ->
|
||||||
|
tr {
|
||||||
|
var runningWidth = 0
|
||||||
|
|
||||||
|
for ((index, column) in columns.withIndex()) {
|
||||||
|
(if (column.fixed) ::th else ::td) {
|
||||||
|
append(column.renderCell(value))
|
||||||
|
|
||||||
|
if (column.fixed) {
|
||||||
|
classList.add("pw-fixed")
|
||||||
|
style.left = "${runningWidth}px"
|
||||||
|
runningWidth += column.width
|
||||||
|
}
|
||||||
|
|
||||||
|
style.width = "${column.width}px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
init {
|
||||||
|
@Suppress("CssUnresolvedCustomProperty", "CssUnusedSymbol")
|
||||||
|
// language=css
|
||||||
|
style("""
|
||||||
|
.pw-table {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: auto;
|
||||||
|
background-color: var(--pw-bg-color);
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table tr {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table thead {
|
||||||
|
position: sticky;
|
||||||
|
display: inline-block;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table thead tr {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table thead th {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th,
|
||||||
|
.pw-table td {
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 3px 6px;
|
||||||
|
border-right: var(--pw-border);
|
||||||
|
border-bottom: var(--pw-border);
|
||||||
|
background-color: var(--pw-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table tbody {
|
||||||
|
user-select: text;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table tbody th,
|
||||||
|
.pw-table tbody td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table tbody th,
|
||||||
|
.pw-table tfoot th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.pw-fixed {
|
||||||
|
position: sticky;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.input {
|
||||||
|
padding: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.input .pw-duration-input {
|
||||||
|
z-index: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.input .pw-duration-input:hover,
|
||||||
|
.pw-table th.input .pw-duration-input:focus-within {
|
||||||
|
margin: -1px;
|
||||||
|
height: calc(100% + 2px);
|
||||||
|
width: calc(100% + 2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.input .pw-duration-input:hover {
|
||||||
|
z-index: 4;
|
||||||
|
border: var(--pw-input-border-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pw-table th.input .pw-duration-input:focus-within {
|
||||||
|
z-index: 6;
|
||||||
|
border: var(--pw-input-border-focus);
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user