use khtml library

This commit is contained in:
2024-03-01 06:10:54 +08:00
parent f48a201406
commit ca1b378a14
4 changed files with 23 additions and 207 deletions

View File

@@ -18,6 +18,7 @@ kotlin {
jsMain {
dependencies {
implementation catalog.klevtree
implementation catalog.khtml
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-serialization-json', version: '1.6.2'
implementation(npm("bootstrap", "5.3.2"))

View File

@@ -1,182 +0,0 @@
package net.woggioni.jpacrepo
import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.startCoroutine
import kotlin.coroutines.suspendCoroutine
import kotlin.js.Promise
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.events.Event
object HtmlUtils {
fun Element.removeChildren() {
while (true) {
removeChild(firstChild ?: break)
}
}
fun Element.on(eventName : String, eventListener : (Event) -> Unit) {
addEventListener(eventName, eventListener)
}
fun launch(block: suspend () -> Unit) {
block.startCoroutine(object : Continuation<Unit> {
override val context: CoroutineContext get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {}
})
}
suspend fun <T> Promise<T>.await(): T = suspendCoroutine { cont ->
then({ cont.resume(it) }, { cont.resumeWithException(it) })
}
}
class HtmlBuilder private constructor(private val doc : Document, val el: Element) {
companion object {
fun <T> of(doc : Document, el: Element, cb : HtmlBuilder.(el : Element) -> T) : T {
return HtmlBuilder(doc, el).cb(el)
}
}
private inline fun <T> dfd(
name : String,
attrs : Map<String, String>,
cb : HtmlBuilder.(el : Element) -> T) : T {
val child = doc.createElement(name)
for((key, value) in attrs) {
child.setAttribute(key, value)
}
el.appendChild(child)
return HtmlBuilder(doc, child).cb(child)
}
fun <T> html(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("html", attrs, cb)
fun <T> head(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("head", attrs, cb)
fun <T> body(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("body", attrs, cb)
fun <T> use(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("use", attrs, cb)
fun <T> svg(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("svg", attrs, cb)
fun <T> div(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("div", attrs, cb)
fun <T> header(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("header", attrs, cb)
fun <T> main(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("main", attrs, cb)
fun <T> footer(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("footer", attrs, cb)
fun <T> a(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("a", attrs, cb)
fun <T> meta(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("meta", attrs, cb)
fun <T> script(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("script", attrs, cb)
fun <T> link(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("link", attrs, cb)
fun <T> title(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("title", attrs, cb)
fun <T> p(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("p", attrs, cb)
fun <T> span(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("span", attrs, cb)
fun <T> i(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("i", attrs, cb)
fun <T> del(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("del", attrs, cb)
fun <T> s(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("s", attrs, cb)
fun <T> ins(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ins", attrs, cb)
fun <T> u(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("u", attrs, cb)
fun <T> b(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("b", attrs, cb)
fun <T> small(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("small", attrs, cb)
fun <T> strong(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("strong", attrs, cb)
fun <T> em(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("em", attrs, cb)
fun <T> mark(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("mark", attrs, cb)
fun <T> obj(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("object", attrs, cb)
fun <T> h1(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h1", attrs, cb)
fun <T> h2(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h2", attrs, cb)
fun <T> h3(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h3", attrs, cb)
fun <T> h4(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h4", attrs, cb)
fun <T> h5(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h5", attrs, cb)
fun <T> h6(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h6", attrs, cb)
fun <T> table(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("table", attrs, cb)
fun <T> thead(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("thead", attrs, cb)
fun <T> tbody(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tbody", attrs, cb)
fun <T> tfoot(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tfoot", attrs, cb)
fun <T> tr(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tr", attrs, cb)
fun <T> th(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("th", attrs, cb)
fun <T> td(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("td", attrs, cb)
fun <T> ol(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ol", attrs, cb)
fun <T> ul(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ul", attrs, cb)
fun <T> li(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("li", attrs, cb)
fun <T> img(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("img", attrs, cb)
fun <T> form(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("form", attrs, cb)
fun <T> label(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("label", attrs, cb)
fun <T> button(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("button", attrs, cb)
fun <T> input(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("input", attrs, cb)
fun <T> select(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("select", attrs, cb)
fun <T> option(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("option", attrs, cb)
fun <T> meter(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("meter", attrs, cb)
fun <T> nav(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("nav", attrs, cb)
fun <T> menu(attrs : Map<String, String> = emptyMap(),
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("menu", attrs, cb)
fun classes(vararg classes : String) {
for(cls in classes) el.classList.add(cls)
}
fun attr(key: String, value : String) {
el.setAttribute(key, value)
}
fun text(txt : String) {
el.appendChild(doc.createTextNode(txt))
}
fun on(eventName : String, cb: (Event) -> Unit) {
el.addEventListener(eventName, cb)
}
}

View File

@@ -6,11 +6,12 @@ import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import net.woggioni.jpacrepo.HtmlUtils.await
import net.woggioni.jpacrepo.HtmlUtils.launch
import net.woggioni.jpacrepo.HtmlUtils.on
import net.woggioni.jpacrepo.HtmlUtils.removeChildren
import net.woggioni.jpacrepo.common.JpacrepoCommons.toHexString
import net.woggioni.khtml.Khtml
import net.woggioni.khtml.Khtml.Companion.await
import net.woggioni.khtml.Khtml.Companion.launch
import net.woggioni.khtml.Khtml.Companion.on
import net.woggioni.khtml.Khtml.Companion.removeChildren
import net.woggioni.klevtree.LevTrie
import org.w3c.dom.CustomEvent
import org.w3c.dom.Element
@@ -132,7 +133,7 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
while (children.length > 0) {
children[0]!!.remove()
}
HtmlBuilder.of(document, root) {
Khtml.of(document, root) {
table {
classes("table", "table-striped", "pkgtable")
thead {
@@ -209,9 +210,9 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
?.let { it[selectedVersion] }
availablePackages?.keys?.first()
}
var selectedCompressionFormat = getFirstAvailableCompressionFormat()
var selectedCompressionFormat: CompressionFormat?
fun HtmlBuilder.createCompressionFormatDropdown() {
fun Khtml.createCompressionFormatDropdown() {
val availablePackages = packages
?.let { it[selectedVersion] }
availablePackages?.keys?.let { compressionFormats ->
@@ -236,24 +237,21 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
val compressionFormatCell = td {
createCompressionFormatDropdown()
el
}
val fileNameCell = td {
selectedPackage?.let {
text(it.second.fileName)
}
el
}
val sizeCell = td {
selectedPackage?.let {
text(it.second.size.toHumanReadableByteSize())
}
el
}
on(REFRESH_TABLE_ROW_EVENT_NAME) { evt ->
on(REFRESH_TABLE_ROW_EVENT_NAME) {
selectedCompressionFormat = getFirstAvailableCompressionFormat()
compressionFormatCell.removeChildren()
HtmlBuilder.of(document, compressionFormatCell) {
Khtml.of(document, compressionFormatCell) {
createCompressionFormatDropdown()
}
selectedPackage = archive.pkgMap[arch]?.let {
@@ -278,9 +276,9 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
}
}
private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
val root = el
private fun Khtml.buildPackageSearchSection(commonElement: Element) {
var selectedPackageName: String? = null
val root = element
div {
classes("row", "g-5")
div {
@@ -354,7 +352,7 @@ private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
}
div { table ->
classes("row", "align-items-md-stretch")
root.on(REFRESH_TABLE_EVENT_NAME) { evt ->
root.on(REFRESH_TABLE_EVENT_NAME) { evt : Event ->
launch {
(evt as? CustomEvent)?.let { pnuv ->
table.let { t ->
@@ -371,7 +369,7 @@ private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
fun main(vararg args: String) {
js("require ('./scss/styles.scss');")
HtmlBuilder.of(document, document.body as HTMLElement) {
Khtml.of(document, document.body as HTMLElement) {
main { main ->
div {
classes("container", "py-4")
@@ -392,7 +390,7 @@ fun main(vararg args: String) {
div {
classes("container")
div {
val commonElement = el
val commonElement = element
classes("row", "align-items-start")
div {
classes("col-3")
@@ -405,7 +403,7 @@ fun main(vararg args: String) {
text("Package Cart")
}
}
fun HtmlBuilder.createSelectedPackageList() {
fun Khtml.createSelectedPackageList() {
if (selectedPackages.isNotEmpty()) {
div {
classes("card-body")
@@ -433,7 +431,7 @@ fun main(vararg args: String) {
}
button {
classes("btn", "btn-primary", "w-100")
el.asDynamic().style.marginTop = "10px"
element.asDynamic().style.marginTop = "10px"
val totalSize = selectedPackages
.asSequence()
.map(PkgTuple::size)
@@ -453,9 +451,9 @@ fun main(vararg args: String) {
.map(PkgTuple::fileName)
.joinToString(" ")
temporaryForm.appendChild(textField)
el.appendChild(temporaryForm)
element.appendChild(temporaryForm)
temporaryForm.submit()
el.removeChild(temporaryForm)
element.removeChild(temporaryForm)
}
}
}
@@ -464,7 +462,7 @@ fun main(vararg args: String) {
div {
createSelectedPackageList()
commonElement.on(REFRESH_SELECTED_PACKAGES_EVENT_NAME) {
el.removeChildren()
element.removeChildren()
createSelectedPackageList()
}
}
@@ -474,7 +472,6 @@ fun main(vararg args: String) {
classes("col-9")
buildPackageSearchSection(commonElement)
}
el
}
}
footer {