Working diff update algorithm
This commit is contained in:
@@ -1,17 +1,14 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "1.4-M2-eap-68"
|
kotlin("multiplatform") version "1.3.71"
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "nl.astraeus"
|
group = "nl.astraeus"
|
||||||
version = "0.1.20-SNAPSHOT"
|
version = "0.1.21-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
jcenter()
|
||||||
url = uri("https://dl.bintray.com/kotlin/kotlin-dev")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@@ -21,6 +18,11 @@ kotlin {
|
|||||||
js {
|
js {
|
||||||
browser {
|
browser {
|
||||||
//produceKotlinLibrary()
|
//produceKotlinLibrary()
|
||||||
|
testTask {
|
||||||
|
useKarma {
|
||||||
|
useChromeHeadless()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +38,12 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib-js"))
|
implementation(kotlin("stdlib-js"))
|
||||||
|
|
||||||
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.2-build-1716")
|
api("org.jetbrains.kotlinx:kotlinx-html-js:0.7.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jsTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test-js"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
komp.iml
2
komp.iml
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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.1.20-SNAPSHOT" 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.1.21-SNAPSHOT" type="JAVA_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package nl.astraeus.komp
|
package nl.astraeus.komp
|
||||||
|
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
|
import org.w3c.dom.HTMLInputElement
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
import org.w3c.dom.NodeList
|
import org.w3c.dom.NodeList
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.get
|
import org.w3c.dom.get
|
||||||
|
|
||||||
const val HASH_VALUE = "komp-hash-value"
|
const val HASH_VALUE = "komp-hash-value"
|
||||||
|
|
||||||
//const val HASH_ATTRIBUTE = "data-komp-hash"
|
//const val HASH_ATTRIBUTE = "data-komp-hash"
|
||||||
const val EVENT_ATTRIBUTE = "data-komp-events"
|
const val EVENT_ATTRIBUTE = "data-komp-events"
|
||||||
|
|
||||||
@@ -59,14 +61,17 @@ object DiffPatch {
|
|||||||
if (oldNode is HTMLElement && newNode is HTMLElement) {
|
if (oldNode is HTMLElement && newNode is HTMLElement) {
|
||||||
if (oldNode.nodeName == newNode.nodeName) {
|
if (oldNode.nodeName == newNode.nodeName) {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Update attributes", oldNode.innerHTML, newNode.innerHTML)
|
console.log("Update attributes", oldNode.nodeName, newNode.nodeName)
|
||||||
}
|
}
|
||||||
updateAttributes(oldNode, newNode);
|
updateAttributes(oldNode, newNode);
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Update children", oldNode.innerHTML, newNode.innerHTML)
|
console.log("Update events", oldNode.nodeName, newNode.nodeName)
|
||||||
|
}
|
||||||
|
updateEvents(oldNode, newNode)
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Update children", oldNode.nodeName, newNode.nodeName)
|
||||||
}
|
}
|
||||||
updateChildren(oldNode, newNode)
|
updateChildren(oldNode, newNode)
|
||||||
updateEvents(oldNode, newNode)
|
|
||||||
oldNode.setKompHash(newNode.getKompHash())
|
oldNode.setKompHash(newNode.getKompHash())
|
||||||
return oldNode
|
return oldNode
|
||||||
}
|
}
|
||||||
@@ -75,20 +80,40 @@ object DiffPatch {
|
|||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Replace node (type)", oldNode.nodeType, oldNode, newNode)
|
console.log("Replace node (type)", oldNode.nodeType, oldNode, newNode)
|
||||||
}
|
}
|
||||||
replaceNode(oldNode, newNode)
|
|
||||||
|
oldNode.parentNode?.replaceChild(newNode, oldNode)
|
||||||
|
//replaceNode(oldNode, newNode)
|
||||||
return newNode
|
return newNode
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateAttributes(oldNode: HTMLElement, newNode: HTMLElement) {
|
private fun updateAttributes(oldNode: HTMLElement, newNode: HTMLElement) {
|
||||||
// removed attributes
|
// removed attributes
|
||||||
for (index in 0 until oldNode.attributes.length) {
|
for (name in oldNode.getAttributeNames()) {
|
||||||
val attr = oldNode.attributes[index]
|
val attr = oldNode.attributes[name]
|
||||||
|
|
||||||
if (attr != null && newNode.attributes[attr.name] == null) {
|
if (attr != null && newNode.getAttribute(name) == null) {
|
||||||
oldNode.removeAttribute(attr.name)
|
oldNode.removeAttribute(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (name in newNode.getAttributeNames()) {
|
||||||
|
val value = newNode.getAttribute(name)
|
||||||
|
val oldValue = oldNode.getAttribute(name)
|
||||||
|
|
||||||
|
if (value != oldValue) {
|
||||||
|
if (value != null) {
|
||||||
|
oldNode.setAttribute(name, value)
|
||||||
|
}else {
|
||||||
|
oldNode.removeAttribute(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newNode is HTMLInputElement && oldNode is HTMLInputElement) {
|
||||||
|
oldNode.value = newNode.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
for (index in 0 until newNode.attributes.length) {
|
for (index in 0 until newNode.attributes.length) {
|
||||||
val attr = newNode.attributes[index]
|
val attr = newNode.attributes[index]
|
||||||
|
|
||||||
@@ -100,6 +125,7 @@ object DiffPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
|
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
|
||||||
@@ -107,7 +133,14 @@ object DiffPatch {
|
|||||||
var newIndex = 0
|
var newIndex = 0
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("updateChildren HTML old/new", oldNode.innerHTML, newNode.innerHTML)
|
console.log(
|
||||||
|
"updateChildren HTML old(${oldNode.childNodes.length})",
|
||||||
|
oldNode.innerHTML
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
"updateChildren HTML new(${newNode.childNodes.length})",
|
||||||
|
newNode.innerHTML
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (newIndex < newNode.childNodes.length) {
|
while (newIndex < newNode.childNodes.length) {
|
||||||
@@ -120,11 +153,11 @@ object DiffPatch {
|
|||||||
val oldChildNode = oldNode.childNodes[oldIndex]
|
val oldChildNode = oldNode.childNodes[oldIndex]
|
||||||
|
|
||||||
if (oldChildNode != null && newChildNode != null) {
|
if (oldChildNode != null && newChildNode != null) {
|
||||||
/*
|
/*
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log(">>> updateChildren old/new", oldChildNode, newChildNode)
|
console.log(">>> updateChildren old/new", oldChildNode, newChildNode)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Update node Old/new", oldChildNode, newChildNode)
|
console.log("Update node Old/new", oldChildNode, newChildNode)
|
||||||
@@ -138,7 +171,7 @@ object DiffPatch {
|
|||||||
val oldHash = oldChildNode.getKompHash()
|
val oldHash = oldChildNode.getKompHash()
|
||||||
val newHash = newChildNode.getKompHash()
|
val newHash = newChildNode.getKompHash()
|
||||||
|
|
||||||
if (newHash != null) {
|
if (newHash >= 0) {
|
||||||
val oldNodeWithNewHashIndex = oldNode.childNodes.findNodeHashIndex(newHash)
|
val oldNodeWithNewHashIndex = oldNode.childNodes.findNodeHashIndex(newHash)
|
||||||
|
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
@@ -146,7 +179,7 @@ object DiffPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldNodeWithNewHashIndex > oldIndex) {
|
if (oldNodeWithNewHashIndex > oldIndex) {
|
||||||
if (oldHash != null) {
|
if (oldHash >= 0) {
|
||||||
val newNodeWithOldHashIndex = newNode.childNodes.findNodeHashIndex(oldHash)
|
val newNodeWithOldHashIndex = newNode.childNodes.findNodeHashIndex(oldHash)
|
||||||
|
|
||||||
// remove i.o. swap
|
// remove i.o. swap
|
||||||
@@ -177,7 +210,7 @@ object DiffPatch {
|
|||||||
oldIndex++
|
oldIndex++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if (oldHash != null && newNode.childNodes.findNodeHashIndex(oldHash) > newIndex) {
|
} else if (oldHash >= 0 && newNode.childNodes.findNodeHashIndex(oldHash) > newIndex) {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("newNodeWithOldHashIndex", oldHash, newNode.childNodes.findNodeHashIndex(oldHash))
|
console.log("newNodeWithOldHashIndex", oldHash, newNode.childNodes.findNodeHashIndex(oldHash))
|
||||||
}
|
}
|
||||||
@@ -189,27 +222,36 @@ object DiffPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNode(oldChildNode, newChildNode)
|
val updatedNode = updateNode(oldChildNode, newChildNode)
|
||||||
|
if (updatedNode == newChildNode) {
|
||||||
|
if (oldChildNode is HTMLElement && newChildNode is HTMLElement) {
|
||||||
|
updateEvents(oldChildNode, newChildNode)
|
||||||
|
}
|
||||||
|
oldIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Null node", oldChildNode, newChildNode)
|
console.log("Null node", oldChildNode, newChildNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldIndex++
|
||||||
|
newIndex++
|
||||||
} else {
|
} else {
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("Append Old/new/node", oldIndex, newIndex, newChildNode)
|
console.log("Append Old/new/node", oldIndex, newIndex, newChildNode)
|
||||||
}
|
}
|
||||||
oldNode.append(newChildNode)
|
oldNode.append(newChildNode)
|
||||||
|
|
||||||
|
oldIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (Komponent.logReplaceEvent) {
|
if (Komponent.logReplaceEvent) {
|
||||||
console.log("<<< Updated Old/new", oldNode.innerHTML, newNode.innerHTML)
|
console.log("<<< Updated Old/new", oldNode.innerHTML, newNode.innerHTML)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
oldIndex++
|
|
||||||
newIndex++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (oldIndex < oldNode.childNodes.length) {
|
while (oldIndex < oldNode.childNodes.length) {
|
||||||
@@ -229,16 +271,25 @@ object DiffPatch {
|
|||||||
|
|
||||||
val newEvents = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
val newEvents = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||||
|
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Update events", oldNode.getAttribute(EVENT_ATTRIBUTE), newNode.getAttribute(EVENT_ATTRIBUTE))
|
||||||
|
}
|
||||||
|
|
||||||
for (event in newEvents) {
|
for (event in newEvents) {
|
||||||
if (event.isNotBlank()) {
|
if (event.isNotBlank()) {
|
||||||
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
|
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
|
||||||
val newNodeEvent = newNode.asDynamic()["event-$event"]
|
val newNodeEvent = newNode.asDynamic()["event-$event"]
|
||||||
if (oldNodeEvent != null) {
|
if (oldNodeEvent != null) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Remove old event $event")
|
||||||
|
}
|
||||||
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
|
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
|
||||||
}
|
}
|
||||||
if (newNodeEvent != null) {
|
if (newNodeEvent != null) {
|
||||||
oldNode.addEventListener(event, newNodeEvent as ((Event) -> Unit), null)
|
if (Komponent.logReplaceEvent) {
|
||||||
oldNode.asDynamic()["event-$event"] = newNodeEvent
|
console.log("Set event $event on", oldNode)
|
||||||
|
}
|
||||||
|
oldNode.setEvent(event, newNodeEvent as ((Event) -> Unit))
|
||||||
}
|
}
|
||||||
oldEvents.remove(event)
|
oldEvents.remove(event)
|
||||||
}
|
}
|
||||||
@@ -261,17 +312,41 @@ object DiffPatch {
|
|||||||
private fun replaceNode(oldNode: Node, newNode: Node) {
|
private fun replaceNode(oldNode: Node, newNode: Node) {
|
||||||
oldNode.parentNode?.also { parent ->
|
oldNode.parentNode?.also { parent ->
|
||||||
val clone = newNode.cloneNode(true)
|
val clone = newNode.cloneNode(true)
|
||||||
if (newNode is HTMLElement) {
|
cloneEvents(clone, newNode)
|
||||||
val events = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
|
||||||
for (event in events) {
|
|
||||||
val foundEvent = newNode.asDynamic()["event-$event"]
|
|
||||||
if (foundEvent != null) {
|
|
||||||
clone.addEventListener(event, foundEvent as ((Event) -> Unit), null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent.replaceChild(clone, oldNode)
|
parent.replaceChild(clone, oldNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun cloneEvents(destination: Node, source: Node) {
|
||||||
|
if (source is HTMLElement && destination is HTMLElement) {
|
||||||
|
val events = (source.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||||
|
for (event in events) {
|
||||||
|
if (event.isNotBlank()) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Clone event $event on", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
val foundEvent = source.asDynamic()["event-$event"]
|
||||||
|
if (foundEvent != null) {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Clone add eventlistener", foundEvent)
|
||||||
|
}
|
||||||
|
destination.setEvent(event, foundEvent as ((Event) -> Unit))
|
||||||
|
} else {
|
||||||
|
if (Komponent.logReplaceEvent) {
|
||||||
|
console.log("Event not found $event", source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index in 0 until source.childNodes.length) {
|
||||||
|
destination.childNodes[index]?.also { destinationChild ->
|
||||||
|
source.childNodes[index]?.also { sourceChild ->
|
||||||
|
cloneEvents(destinationChild, sourceChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import org.w3c.dom.events.Event
|
|||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun HTMLElement.setEvent(name: String, noinline callback: (Event) -> Unit): Unit {
|
inline fun HTMLElement.setEvent(name: String, noinline callback: (Event) -> Unit): Unit {
|
||||||
val eventName = if (name.startsWith("on")) {
|
val eventName = if (name.startsWith("on")) {
|
||||||
name.substring(2)
|
name.substring(2)
|
||||||
} else {
|
} else {
|
||||||
@@ -103,19 +103,19 @@ class HtmlBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tag.attributesEntries.forEach {
|
for ((key, value) in tag.attributesEntries) {
|
||||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
if (key == "class") {
|
||||||
val key_value = "${it.key}-${it.value}"
|
val classes = value.split(Regex("\\s+"))
|
||||||
hash = hash * 37 + key_value.hashCode()
|
|
||||||
}
|
|
||||||
if (it.key == "class") {
|
|
||||||
val classes = it.value.split(Regex("\\s+"))
|
|
||||||
val classNames = StringBuilder()
|
val classNames = StringBuilder()
|
||||||
|
|
||||||
for (cls in classes) {
|
for (cls in classes) {
|
||||||
val cssStyle = komponent.declaredStyles[cls]
|
val cssStyle = komponent.declaredStyles[cls]
|
||||||
|
|
||||||
if (cssStyle != null) {
|
if (cssStyle != null) {
|
||||||
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
|
hash = hash * 37 + cssStyle.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
if (cls.endsWith(":hover")) {
|
if (cls.endsWith(":hover")) {
|
||||||
val oldOnMouseOver = element.onmouseover
|
val oldOnMouseOver = element.onmouseover
|
||||||
val oldOnMouseOut = element.onmouseout
|
val oldOnMouseOut = element.onmouseout
|
||||||
@@ -138,15 +138,31 @@ class HtmlBuilder(
|
|||||||
element.setStyles(cssStyle)
|
element.setStyles(cssStyle)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
|
hash = hash * 37 + cls.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
classNames.append(cls)
|
classNames.append(cls)
|
||||||
classNames.append(" ")
|
classNames.append(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
element.className = classNames.toString()
|
element.className = classNames.toString()
|
||||||
} else {
|
|
||||||
element.setAttribute(it.key, it.value)
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
|
val key_value = "${key}-${classNames}"
|
||||||
|
hash = hash * 37 + key_value.hashCode()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
element.setAttribute(key, value)
|
||||||
|
|
||||||
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
|
val key_value = "${key}-${value}"
|
||||||
|
hash = hash * 37 + key_value.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package nl.astraeus.komp
|
package nl.astraeus.komp
|
||||||
|
|
||||||
import kotlinx.html.Tag
|
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import org.w3c.dom.HTMLDivElement
|
import org.w3c.dom.HTMLDivElement
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
@@ -8,20 +7,21 @@ import org.w3c.dom.Node
|
|||||||
import org.w3c.dom.css.CSSStyleDeclaration
|
import org.w3c.dom.css.CSSStyleDeclaration
|
||||||
import kotlin.browser.document
|
import kotlin.browser.document
|
||||||
|
|
||||||
public typealias CssStyle = CSSStyleDeclaration.() -> Unit
|
typealias CssStyle = CSSStyleDeclaration.() -> Unit
|
||||||
|
|
||||||
fun Tag.include(component: Komponent) {
|
fun HtmlConsumer.include(component: Komponent) {
|
||||||
|
if (Komponent.updateStrategy == UpdateStrategy.REPLACE) {
|
||||||
if (component.element != null) {
|
if (component.element != null) {
|
||||||
component.update()
|
component.update()
|
||||||
} else {
|
} else {
|
||||||
component.refresh()
|
component.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
val consumer = this.consumer
|
component.element?.also {
|
||||||
val element = component.element
|
append(it)
|
||||||
|
}
|
||||||
if (consumer is HtmlBuilder && element != null) {
|
} else {
|
||||||
consumer.append(element)
|
append(component.create())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,10 +47,6 @@ abstract class Komponent {
|
|||||||
consumer.render()
|
consumer.render()
|
||||||
val result = consumer.finalize()
|
val result = consumer.finalize()
|
||||||
|
|
||||||
if (logReplaceEvent) {
|
|
||||||
console.log("Element hash", result, result.getKompHash())
|
|
||||||
}
|
|
||||||
|
|
||||||
element = result
|
element = result
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
132
src/jsTest/kotlin/nl/astraeus/komp/TestUpdate.kt
Normal file
132
src/jsTest/kotlin/nl/astraeus/komp/TestUpdate.kt
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package nl.astraeus.komp
|
||||||
|
|
||||||
|
import kotlinx.html.*
|
||||||
|
import kotlinx.html.js.onClickFunction
|
||||||
|
import org.w3c.dom.HTMLElement
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
import org.w3c.dom.get
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
fun nodesEqual(node1: Node, node2: Node): Boolean {
|
||||||
|
if (node1.childNodes.length != node1.childNodes.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (node1 is HTMLElement && node2 is HTMLElement) {
|
||||||
|
if (node1.attributes.length != node2.attributes.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (index in 0 until node1.attributes.length) {
|
||||||
|
node1.attributes[index]?.also { attr1 ->
|
||||||
|
val attr2 = node2.getAttribute(attr1.name)
|
||||||
|
|
||||||
|
if (attr1.value != attr2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index in 0 until node1.childNodes.length) {
|
||||||
|
node1.childNodes[index]?.also { child1 ->
|
||||||
|
node2.childNodes[index]?.also { child2 ->
|
||||||
|
if (!nodesEqual(child1, child2)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestUpdate {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCompare1() {
|
||||||
|
val dom1 = HtmlBuilder.create {
|
||||||
|
div {
|
||||||
|
div(classes = "bla") {
|
||||||
|
span {
|
||||||
|
+" Some Text "
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
+"Table column"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dom2 = HtmlBuilder.create {
|
||||||
|
div {
|
||||||
|
span {
|
||||||
|
id = "123"
|
||||||
|
|
||||||
|
+"New dom!"
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
value = "bla"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiffPatch.updateNode(dom1, dom2)
|
||||||
|
|
||||||
|
assertTrue(nodesEqual(dom1, dom2), "Updated dom not equal to original")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCompare2() {
|
||||||
|
val dom1 = HtmlBuilder.create {
|
||||||
|
div {
|
||||||
|
div(classes = "bla") {
|
||||||
|
span {
|
||||||
|
+" Some Text "
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
th {
|
||||||
|
+ "Header"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
+"Table column"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dom2 = HtmlBuilder.create {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
span {
|
||||||
|
+ "Other text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
id = "123"
|
||||||
|
|
||||||
|
+"New dom!"
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
value = "bla"
|
||||||
|
|
||||||
|
onClickFunction = {
|
||||||
|
println("Clickerdyclick!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Komponent.logReplaceEvent = true
|
||||||
|
DiffPatch.updateNode(dom1, dom2)
|
||||||
|
|
||||||
|
assertTrue(nodesEqual(dom1, dom2), "Updated dom not equal to original")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user