This commit is contained in:
2021-07-11 13:29:34 +02:00
parent 48708580ca
commit dac465a161
10 changed files with 169 additions and 75 deletions

View File

@@ -5,7 +5,7 @@ plugins {
}
group = "nl.astraeus"
version = "0.4.1"
version = "0.5.2"
repositories {
mavenCentral()

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:commonMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.4.1" type="JAVA_MODULE" version="4">
<module external.linked.project.id="komp:commonMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.5.1" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.6]/Native []/Native [general]" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.8]/Native []/Native [general]" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<compilerSettings />
<compilerArguments>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:commonTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.4.1" type="JAVA_MODULE" version="4">
<module external.linked.project.id="komp:commonTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.5.1" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.6]/Native []/Native [general]" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<configuration version="3" platform="Common (experimental) " allPlatforms="JS []/JVM [1.8]/Native []/Native [general]" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">
<newMppModelJpsModuleKind>SOURCE_SET_HOLDER</newMppModelJpsModuleKind>
<externalSystemTestTasks>
<externalSystemTestTask>jsLegacyBrowserTest|komp:jsTest|jsLegacy</externalSystemTestTask>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.version="0.4.1" type="JAVA_MODULE" version="4">
<module external.linked.project.id="komp" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.version="0.5.1" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">

View File

@@ -73,6 +73,12 @@
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
<artifact type="jar" name="komp-jslegacy-0.5.1">
<output-path>$PROJECT_DIR$/build/libs</output-path>
<root id="archive" name="komp-jslegacy-0.5.1.jar">
<element id="module-output" name="komp.jsMain" />
</root>
</artifact>
</component>
<component name="CheckStyle-IDEA">
<option name="configuration">

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:jsMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.4.1" type="JAVA_MODULE" version="4">
<module external.linked.project.id="komp:jsMain" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.5.1" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="false" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="komp:jsTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.4.1" type="JAVA_MODULE" version="4">
<module external.linked.project.id="komp:jsTest" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="nl.astraeus" external.system.module.type="sourceSet" external.system.module.version="0.5.1" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JavaScript " allPlatforms="JS []" useProjectSettings="false" isTestModule="true" externalProjectId="komp" pureKotlinSourceFolders="$MODULE_DIR$/src/jsMain/kotlin;/home/rnentjes/Development/komp/komp/build/externals/komp-js-legacy/src;/home/rnentjes/Development/komp/komp/build/externals/komp-js-ir/src;/home/rnentjes/Development/komp/komp/src/jsTest/kotlin">

View File

@@ -9,6 +9,7 @@ import kotlinx.html.TagConsumer
import kotlinx.html.Unsafe
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSpanElement
import org.w3c.dom.Node
import org.w3c.dom.asList
@@ -20,6 +21,7 @@ private var currentElement: Element? = null
interface HtmlConsumer : TagConsumer<Element> {
fun append(node: Element)
fun include(komponent: Komponent)
fun debug(block: HtmlConsumer.() -> Unit)
}
fun Int.asSpaces(): String {
@@ -30,7 +32,8 @@ fun Int.asSpaces(): String {
return result.toString()
}
fun FlowOrMetaDataOrPhrasingContent.currentElement(): Element = (currentElement as? Element) ?: error("No current element defined!")
fun FlowOrMetaDataOrPhrasingContent.currentElement(): Element =
currentElement ?: error("No current element defined!")
fun Element.printTree(indent: Int = 0): String {
val result = StringBuilder()
@@ -83,15 +86,25 @@ fun Element.printTree(indent: Int = 0): String {
return result.toString()
}
fun Element.clearKompAttributes() {
this.asDynamic()["komp-attributes"] = mutableListOf<String>()
private fun Element.clearKompAttributes() {
val attributes = this.asDynamic()["komp-attributes"] as MutableSet<String>?
if (attributes == null) {
this.asDynamic()["komp-attributes"] = mutableSetOf<String>()
} else {
attributes.clear()
}
if (this is HTMLInputElement) {
this.checked = false
}
}
fun Element.getKompAttributes(): MutableList<String> {
var result: MutableList<String>? = this.asDynamic()["komp-attributes"] as MutableList<String>?
private fun Element.getKompAttributes(): MutableSet<String> {
var result: MutableSet<String>? = this.asDynamic()["komp-attributes"] as MutableSet<String>?
if (result == null) {
result = mutableListOf()
result = mutableSetOf()
this.asDynamic()["komp-attributes"] = result
}
@@ -99,28 +112,43 @@ fun Element.getKompAttributes(): MutableList<String> {
return result
}
fun Element.setKompAttribute(name: String, value: String) {
val setAttrs: MutableList<String> = getKompAttributes()
private fun Element.setKompAttribute(name: String, value: String) {
val setAttrs: MutableSet<String> = getKompAttributes()
setAttrs.add(name)
if (this.getAttribute(name) != value) {
if (Komponent.logReplaceEvent) {
console.log("Setting attribute [$name,$value] on $this (old: ${this.getAttribute(name)})")
}
if (this is HTMLInputElement) {
when (name) {
"checked" -> {
this.checked = value == "checked"
}
"value" -> {
this.value = value
}
else -> {
setAttribute(name, value)
}
}
} else if (this.getAttribute(name) != value) {
setAttribute(name, value)
}
}
fun Element.clearKompEvents() {
private fun Element.clearKompEvents() {
for ((name, event) in getKompEvents()) {
currentElement?.removeEventListener(name, event)
}
this.asDynamic()["komp-events"] = mutableMapOf<String, (Event) -> Unit>()
val events = this.asDynamic()["komp-events"] as MutableMap<String, (Event) -> Unit>?
if (events == null) {
this.asDynamic()["komp-events"] = mutableMapOf<String, (Event) -> Unit>()
} else {
events.clear()
}
}
fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
private fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
val eventName: String = if (name.startsWith("on")) {
name.substring(2)
} else {
@@ -138,23 +166,19 @@ fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
this.asDynamic()["komp-events"] = events
if (Komponent.logReplaceEvent) {
console.log("Setting events [$eventName] on $this")
}
this.addEventListener(eventName, event)
}
fun Element.getKompEvents(): MutableMap<String, (Event) -> Unit> {
private fun Element.getKompEvents(): MutableMap<String, (Event) -> Unit> {
return this.asDynamic()["komp-events"] ?: mutableMapOf()
}
data class ElementIndex(
private data class ElementIndex(
val parent: Node,
var childIndex: Int
)
fun ArrayList<ElementIndex>.currentParent(): Node {
private fun ArrayList<ElementIndex>.currentParent(): Node {
this.lastOrNull()?.let {
return it.parent
}
@@ -162,7 +186,7 @@ fun ArrayList<ElementIndex>.currentParent(): Node {
throw IllegalStateException("currentParent should never be null!")
}
fun ArrayList<ElementIndex>.currentElement(): Node? {
private fun ArrayList<ElementIndex>.currentElement(): Node? {
this.lastOrNull()?.let {
return it.parent.childNodes[it.childIndex]
}
@@ -170,21 +194,21 @@ fun ArrayList<ElementIndex>.currentElement(): Node? {
return null
}
fun ArrayList<ElementIndex>.nextElement() {
private fun ArrayList<ElementIndex>.nextElement() {
this.lastOrNull()?.let {
it.childIndex++
}
}
fun ArrayList<ElementIndex>.pop() {
private fun ArrayList<ElementIndex>.pop() {
this.removeLast()
}
fun ArrayList<ElementIndex>.push(element: Node) {
private fun ArrayList<ElementIndex>.push(element: Node) {
this.add(ElementIndex(element, 0))
}
fun ArrayList<ElementIndex>.replace(new: Node) {
private fun ArrayList<ElementIndex>.replace(new: Node) {
if (this.currentElement() != null) {
this.currentElement()?.parentElement?.replaceChild(new, this.currentElement()!!)
} else {
@@ -192,22 +216,22 @@ fun ArrayList<ElementIndex>.replace(new: Node) {
}
}
fun Node.asElement() = this as? HTMLElement
private fun Node.asElement() = this as? HTMLElement
class HtmlBuilder(
val parent: Element,
var childIndex: Int = 0
) : HtmlConsumer {
private var currentPosition = arrayListOf<ElementIndex>()
private var inDebug = false
var currentNode: Node? = null
var root: Element? = null
val currentAttributes: MutableMap<String, String> = mutableMapOf()
init {
currentPosition.add(ElementIndex(parent, childIndex))
}
constructor(position: ElementIndex) : this(position.parent as Element, position.childIndex)
override fun include(komponent: Komponent) {
komponent.create(
currentPosition.last().parent as Element,
@@ -221,8 +245,18 @@ class HtmlBuilder(
currentPosition.nextElement()
}
override fun debug(block: HtmlConsumer.() -> Unit) {
inDebug = true
try {
block.invoke(this)
} finally {
inDebug = false
}
}
fun logReplace(msg: String) {
if (Komponent.logReplaceEvent) {
if (Komponent.logReplaceEvent && inDebug) {
console.log(msg)
}
}
@@ -251,9 +285,9 @@ class HtmlBuilder(
//logReplace"onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}")
//logReplace"onTagStart, currentElement, replace: ${currentNode?.asElement()?.tagName} -> ${tag.tagName}")
currentNode = if (tag.namespace != null) {
document.createElementNS(tag.namespace, tag.tagName) as Element
document.createElementNS(tag.namespace, tag.tagName)
} else {
document.createElement(tag.tagName) as Element
document.createElement(tag.tagName)
}
currentPosition.replace(currentNode!!)
@@ -273,7 +307,7 @@ class HtmlBuilder(
currentElement?.clearKompEvents()
for (entry in tag.attributesEntries) {
currentElement!!.setKompAttribute(entry.key, entry.value)
currentElement!!.setKompAttribute(entry.key.lowercase(), entry.value)
}
}
@@ -294,13 +328,14 @@ class HtmlBuilder(
}
override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) {
//logReplace"onTagAttributeChange, ${tag.tagName} [$attribute, $value]")
logReplace("onTagAttributeChange, ${tag.tagName} [$attribute, $value]")
checkTag(tag)
if (value == null) {
currentElement?.removeAttribute(attribute)
currentElement?.removeAttribute(attribute.lowercase())
} else {
currentElement?.setKompAttribute(attribute, value)
currentElement?.setKompAttribute(attribute.lowercase(), value)
}
}
@@ -309,37 +344,46 @@ class HtmlBuilder(
checkTag(tag)
currentElement?.setKompEvent(event, value)
currentElement?.setKompEvent(event.lowercase(), value)
}
override fun onTagEnd(tag: Tag) {
//logReplace"onTagEnd: ${tag.tagName}, $currentElement")
//logReplace"onTagEnd: ${tag.tagName}, $currentPosition")
while (currentPosition.currentElement() != null) {
currentPosition.currentElement()?.let {
//logReplace"Removing $it")
it.parentElement?.removeChild(it)
}
}
checkTag(tag)
//logReplace"onTagEnd, pre-pop: $currentPosition")
currentPosition.pop()
//logReplace"onTagEnd, post-pop: $currentPosition")
val setAttrs: List<String> = currentElement.asDynamic()["komp-attributes"] ?: listOf()
// remove attributes that where not set
if (currentElement?.hasAttributes() == true) {
for (index in 0 until (currentElement?.attributes?.length ?: 0)) {
val attr = currentElement?.attributes?.get(index)
val element = currentElement
if (element?.hasAttributes() == true) {
for (index in 0 until element.attributes.length) {
val attr = element.attributes[index]
if (attr != null) {
if (element is HTMLElement && attr.name == "data-has-focus" && "true" == attr.value) {
element.focus()
}
if (!setAttrs.contains(attr.name)) {
currentElement?.removeAttribute(attr.name)
if (element is HTMLInputElement) {
if (attr.name == "checkbox") {
element.checked = false
} else if (attr.name == "value") {
element.value = ""
}
} else {
if (Komponent.logReplaceEvent) {
console.log("Clear attribute [${attr.name}] on $element)")
}
element.removeAttribute(attr.name)
}
}
}
}
@@ -360,8 +404,10 @@ class HtmlBuilder(
}
//logReplace"Tag content: $content")
if (currentElement?.textContent != content.toString()) {
if (
currentElement?.nodeType != Node.TEXT_NODE ||
currentElement?.textContent != content.toString()
) {
currentElement?.textContent = content.toString()
}
@@ -376,7 +422,7 @@ class HtmlBuilder(
}
val s = document.createElement("span") as HTMLSpanElement
s.innerText = entity.text
s.innerHTML = entity.text
currentPosition.replace(
s.childNodes.asList().firstOrNull() ?: document.createTextNode(entity.text)
)

View File

@@ -1,7 +1,6 @@
package nl.astraeus.komp
import kotlinx.browser.window
import kotlinx.html.div
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
@@ -29,14 +28,6 @@ class StateDelegate<T>(
inline fun <reified T> Komponent.state(initialValue: T): StateDelegate<T> = StateDelegate(this, initialValue)
class DummyKomponent : Komponent() {
override fun HtmlBuilder.render() {
div {
+"dummy"
}
}
}
enum class UnsafeMode {
UNSAFE_ALLOWED,
UNSAFE_DISABLED,
@@ -50,12 +41,15 @@ abstract class Komponent {
var element: Node? = null
val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap()
open fun create(parent: Element, childIndex: Int = 0) {
//parent.parentElement?.child
val builder = HtmlBuilder(parent, childIndex)
open fun create(parent: Element, childIndex: Int? = null) {
val builder = HtmlBuilder(
parent,
childIndex ?: parent.childElementCount
)
builder.render()
element = builder.root
onAfterUpdate()
}
abstract fun HtmlBuilder.render()
@@ -63,7 +57,7 @@ abstract class Komponent {
/**
* This method is called after the Komponent is updated
*
* note: it's not called at first render
* note: it's also called at first render
*/
open fun onAfterUpdate() {}
@@ -173,7 +167,7 @@ abstract class Komponent {
}
}
} else {
console.log("Komponent element is null", next)
console.log("Komponent element is null", next, element)
}
}
}

View File

@@ -2,6 +2,7 @@ package nl.astraeus.komp
import kotlinx.browser.document
import kotlinx.html.div
import kotlinx.html.i
import kotlinx.html.id
import kotlinx.html.js.onClickFunction
import kotlinx.html.p
@@ -103,8 +104,55 @@ class SimpleKomponent : Komponent() {
}
class ReplaceKomponent : Komponent() {
var includeSpan = true
override fun HtmlBuilder.render() {
div {
+"Child 2"
div {
if (includeSpan) {
span {
i("fas fa-eye") {
+"span1"
}
}
span {
i("fas fa-eye") {
+"span2"
}
}
span {
i("fas fa-eye") {
+"span3"
}
}
}
}
}
}
}
class TestUpdate {
@Test
fun testUpdateWithEmpty() {
val div = document.createElement("div") as HTMLDivElement
val rk = ReplaceKomponent()
Komponent.logRenderEvent = true
Komponent.create(div, rk)
println("ReplaceKomponent: ${div.printTree()}")
rk.includeSpan = false
rk.requestImmediateUpdate()
println("ReplaceKomponent: ${div.printTree()}")
}
@Test
fun testSimpleKomponent() {
val sk = SimpleKomponent()