From 29f570cfc12545e3b1bc96ab2bd6525a373c2c74 Mon Sep 17 00:00:00 2001 From: rnentjes Date: Fri, 15 Nov 2019 19:06:02 +0100 Subject: [PATCH] - Keeping it simple, just html element replace - Updated Kotlin version --- build.gradle | 8 +- komp.iml | 23 +- komp.ipr | 28 +- src/main/kotlin/nl/astraeus/komp/DomDiff.kt | 91 ----- .../kotlin/nl/astraeus/komp/KompElement.kt | 359 ------------------ src/main/kotlin/nl/astraeus/komp/Komponent.kt | 90 +---- .../kotlin/nl/astraeus/komp/SizedKomponent.kt | 55 --- 7 files changed, 46 insertions(+), 608 deletions(-) delete mode 100644 src/main/kotlin/nl/astraeus/komp/DomDiff.kt delete mode 100644 src/main/kotlin/nl/astraeus/komp/KompElement.kt delete mode 100644 src/main/kotlin/nl/astraeus/komp/SizedKomponent.kt diff --git a/build.gradle b/build.gradle index d0de31e..f24f9fd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ group 'nl.astraeus' -version '0.1.6-SNAPSHOT' +version '0.1.7' apply plugin: 'kotlin2js' apply plugin: 'kotlin-dce-js' @@ -21,11 +21,11 @@ repositories { } ext { - kotlin_version = '1.2.51' + kotlin_version = '1.3.50' } buildscript { - ext.kotlin_version = '1.2.51' + ext.kotlin_version = '1.3.50' repositories { maven { url "http://nexus.astraeus.nl/nexus/content/groups/public" @@ -39,7 +39,7 @@ buildscript { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" - compile 'org.jetbrains.kotlinx:kotlinx-html-js:0.6.4' + compile 'org.jetbrains.kotlinx:kotlinx-html-js:0.6.10' } uploadArchives { diff --git a/komp.iml b/komp.iml index b220706..140cd6f 100644 --- a/komp.iml +++ b/komp.iml @@ -1,8 +1,8 @@ - + - + $MODULE_DIR$/build/classes/kotlin/test/komp_test.js @@ -13,15 +13,14 @@ - @@ -34,18 +33,18 @@ - - - + + + - - - - + + + + \ No newline at end of file diff --git a/komp.ipr b/komp.ipr index 4f12ad8..b9e8c37 100644 --- a/komp.ipr +++ b/komp.ipr @@ -41,9 +41,12 @@ + @@ -149,40 +153,40 @@ - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/src/main/kotlin/nl/astraeus/komp/DomDiff.kt b/src/main/kotlin/nl/astraeus/komp/DomDiff.kt deleted file mode 100644 index 9fe52bf..0000000 --- a/src/main/kotlin/nl/astraeus/komp/DomDiff.kt +++ /dev/null @@ -1,91 +0,0 @@ -package nl.astraeus.komp - -import org.w3c.dom.Node -import org.w3c.dom.get - -/** - * User: rnentjes - * Date: 10-9-17 - * Time: 13:33 - */ - -object DomDiffer { - - fun replaceDiff(oldElement: KompElement, newElement: KompElement, element: Node): Node { - if (oldElement.isKomponent() && newElement.isKomponent()) { - if (oldElement.equals(newElement)) { - newElement.komponent?.update() - - return newElement.komponent?.element ?: newElement.komponent?.create()?.create() - ?: throw IllegalStateException("Unable to create new element!") - } else { - return Komponent.replaceNode(newElement, element) - } - } else if (!oldElement.isKomponent() && newElement.isKomponent()) { - return Komponent.replaceNode(newElement, element) - } else if (!oldElement.equals(newElement)) { - return Komponent.replaceNode(newElement, element) - } else { - if (oldElement.children == null && newElement.children != null) { - for (index in 0 until newElement.children.size) { - Komponent.appendElement(element, newElement.children[index]) - } - } else if (oldElement.children != null && newElement.children == null) { - while (element.firstChild != null) { - if (Komponent.logReplaceEvent) { - console.log("Remove", element.firstChild) - } - element.removeChild(element.firstChild!!) - } - } else if (oldElement.children != null && newElement.children != null) { - if (oldElement.children.size > newElement.children.size) { - val toRemove = oldElement.children.size - newElement.children.size - var removed = 0 - var index = 0 - - while (index < newElement.children.size) { - val childNode = element.childNodes[index] - - if (childNode == null) { - println("Warn childNode is null!") - } else { - if ((!oldElement.children[index + removed].equals(newElement.children[index])) && removed < toRemove) { - if (Komponent.logReplaceEvent) { - console.log("Remove", oldElement.children[index + removed], newElement.children[index]) - } - element.removeChild(childNode) - - removed++ - } else { - replaceDiff(oldElement.children[index + removed], newElement.children[index], childNode) - - index++ - } - } - } - - while (removed < toRemove) { - element.lastChild?.also { - Komponent.removeElement(it) - } - - removed++ - } - } else { - for (index in 0 until newElement.children.size) { - val childNode = element.childNodes[index] - - if (childNode == null) { - Komponent.appendElement(element, newElement.children[index]) - } else { - replaceDiff(oldElement.children[index], newElement.children[index], childNode) - } - } - } - } - - return element - } - } - -} diff --git a/src/main/kotlin/nl/astraeus/komp/KompElement.kt b/src/main/kotlin/nl/astraeus/komp/KompElement.kt deleted file mode 100644 index a1c79b6..0000000 --- a/src/main/kotlin/nl/astraeus/komp/KompElement.kt +++ /dev/null @@ -1,359 +0,0 @@ -package nl.astraeus.komp - -import kotlinx.html.Entities -import kotlinx.html.Tag -import kotlinx.html.TagConsumer -import kotlinx.html.Unsafe -import org.w3c.dom.HTMLElement -import org.w3c.dom.Node -import org.w3c.dom.events.Event -import org.w3c.dom.get -import kotlin.browser.document -import kotlin.dom.isElement - -/** - * User: rnentjes - * Date: 8-5-18 - * Time: 21:58 - */ - -enum class ElementType { - KOMPONENT, - TAG, - TEXT, - UNSAFE, -} - -class KompElement( - val type: ElementType, - val komponent: Komponent, - var text: String, - var attributes: MutableMap? = null, - val children: MutableList? = null, - val events: MutableMap Unit>? = null -) { - - constructor(komponent: Komponent, text: String, type: ElementType) : this( - type, - komponent, - text, - if (type == ElementType.TAG) { - HashMap() - } else { - null - }, - if (type == ElementType.TAG) { - ArrayList() - } else { - null - }, - if (type == ElementType.TAG) { - HashMap() - } else { - null - } - ) - - constructor(komponent: Komponent) : this( - ElementType.KOMPONENT, - komponent, - "", - null, - null, - null - ) - - /* shallow equals check */ - fun equals(other: KompElement): Boolean { - if (other.isKomponent() && isKomponent()) { - val result = komponent == other.komponent - if (!result && Komponent.logEquals) { - console.log("!= komponent", this, other) - } - return result - } else if (other.isText() || isText()) { - if (other.text != text && Komponent.logEquals) { - console.log("!= text", this, other) - } - return other.text == text - } else { - if (other.attributes?.size != attributes?.size || other.events?.size != events?.size) { - if (Komponent.logEquals) { - console.log("!= attr size or event size", this, other) - } - return false - } else { - (attributes?.entries)?.forEach { entry -> - if (!other.attributes?.get(entry.key).equals(entry.value)) { - if (Komponent.logEquals) { - console.log("!= attr", this, other) - } - return false - } - } - (events?.entries)?.forEach { entry -> - val thisFunction: dynamic = other.events?.get(entry.key) - val otherFunction: dynamic = entry.value - - if (thisFunction != null && thisFunction.callableName != undefined) { - val result = thisFunction.callableName == otherFunction.callableName - if (!result && Komponent.logEquals) { - console.log("!= event", thisFunction, otherFunction) - } - return result - } - - if (Komponent.logEquals) { - console.log("!= event, events have no callableName", thisFunction, otherFunction) - } - return false - } - } - } - - return true - } - - fun isText(): Boolean { - return type == ElementType.TEXT - } - - fun isKomponent(): Boolean { - return type == ElementType.KOMPONENT - } - - fun create(svg: Boolean = false): Node = when(type) { - ElementType.KOMPONENT -> { - val komp = komponent - - val kompElement = komp.create() - val element = kompElement.create() - - komp.kompElement = kompElement - komp.element = element - - element - } - ElementType.TEXT -> document.createTextNode(text) - ElementType.UNSAFE -> { - val div = if (svg) { - document.createElementNS("http://www.w3.org/2000/svg","svg") - } else { - document.createElement("div") - } - var result: Node? = null - - div.innerHTML = text - - //console.log("div element with unsafe innerHTML", div) - - for (index in 0 until div.childNodes.length) { - val node = div.childNodes[index]!! - - //console.log("$index -> ", node) - - if (node.isElement) { - if (result != null) { - throw IllegalStateException("Only one element allowed in unsafe block!") - } - result = node - } - } - - result ?: throw IllegalStateException("No element found in unsafe content! [$text]") - } - ElementType.TAG -> { - val svg = text == "svg" - val result = if (svg) { - document.createElementNS("http://www.w3.org/2000/svg", text) - } else { - document.createElement(text) - } - - (attributes?.entries)?.forEach { entry -> - if (entry.key == "class") { - val classes = entry.value.split(" ") - val classNames = StringBuilder() - - for (cls in classes) { - val cssStyle = komponent?.declaredStyles?.get(cls) - - if (cssStyle != null) { - if (result is HTMLElement) { - for (index in 0 until cssStyle.length) { - val propertyName = cssStyle.item(index) - result.style.setProperty(propertyName, cssStyle.getPropertyValue(propertyName)) - } - } - } else { - classNames.append(cls) - classNames.append(" ") - } - } - - if (result !is HTMLElement) { - result.setAttribute(entry.key, entry.value) - } else { - result.className = classNames.toString() - } - } else { - result.setAttribute(entry.key, entry.value) - } - } - - (events?.entries)?.forEach { event -> - val key = if (event.key.startsWith("on")) { - event.key.substring(2) - } else { - event.key - } - result.addEventListener(key, event.value) - } - - children?.forEach { child -> - result.append(child.create(svg)) - } - - result - } - } - - override fun toString(): String { - return this.toString("") - } - - fun toString(indent: String = ""): String { - val result = StringBuilder() - - if (attributes != null) { - result.append(indent) - result.append("<") - } - result.append(text) - attributes?.also { attributes -> - for (entry in attributes.entries) { - result.append("\n") - result.append(indent) - result.append(indent) - result.append(entry.key) - result.append("=\"") - result.append(entry.value) - result.append("\"") - } - events?.apply { - for (event in this.entries) { - result.append("\n") - result.append(indent) - result.append(indent) - result.append(event.key) - result.append("=") - result.append(event.value) - } - } - result.append("\n") - result.append(indent) - result.append(">") - - children?.apply { - result.append("\n") - result.append(indent) - for (child in this) { - result.append(child.toString(" $indent")) - } - } - - result.append("\n") - result.append(indent) - result.append("") - } - - return result.toString() - } -} - -class UnsafeWrapper: Unsafe { - var text = "" - - override fun String.unaryPlus() { - text += this@unaryPlus - } -} - -class KompConsumer( - val komponent: Komponent -) : TagConsumer { - val stack = ArrayList() - var currentTag: KompElement? = null - - override fun finalize(): KompElement { - return currentTag!! - } - - override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) { - //console.log("KC.onTagAttributeChange", tag, attribute, value) - if (value != null) { - currentTag?.attributes?.put(attribute, value) - } - } - - override fun onTagContent(content: CharSequence) { - //console.log("KC.onTagContent", content) - - currentTag?.children?.add(KompElement(komponent, content.toString(), ElementType.TEXT)) - } - - override fun onTagContentEntity(entity: Entities) { - //console.log("KC.onTagContentEntity", entity) - } - - override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { - //console.log("KC.onTagContentUnsafe", block) - val txt = UnsafeWrapper() - - block.invoke(txt) - - //console.log("KC.onTagContentUnsafe", txt) - currentTag?.children?.add(KompElement(komponent, txt.text, ElementType.UNSAFE)) - } - - override fun onTagEnd(tag: Tag) { - //console.log("KC.onTagEnd", tag) - - check(currentTag != null) - check(currentTag?.children != null) - - val ke = currentTag - if (stack.isNotEmpty()) { - currentTag = stack.removeAt(stack.lastIndex) - - if (ke != null) { - currentTag?.children?.add(ke) - } - } - } - - override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) { - //console.log("KC.onTagEvent", tag, event, value) - - currentTag?.events?.put(event, value) - } - - override fun onTagStart(tag: Tag) { - //console.log("KC.onTagStart", tag) - - currentTag?.apply { - stack.add(this) - } - - currentTag = KompElement(komponent, tag.tagName, ElementType.TAG) - - currentTag?.attributes = tag.attributes - } - - fun appendKomponent(komponent: Komponent) { - currentTag?.children?.add(KompElement(komponent)) - } - -} diff --git a/src/main/kotlin/nl/astraeus/komp/Komponent.kt b/src/main/kotlin/nl/astraeus/komp/Komponent.kt index eaa41bd..287274c 100644 --- a/src/main/kotlin/nl/astraeus/komp/Komponent.kt +++ b/src/main/kotlin/nl/astraeus/komp/Komponent.kt @@ -1,6 +1,8 @@ package nl.astraeus.komp import kotlinx.html.HtmlBlockTag +import kotlinx.html.TagConsumer +import kotlinx.html.dom.create import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLElement import org.w3c.dom.Node @@ -8,10 +10,7 @@ import org.w3c.dom.css.CSSStyleDeclaration import kotlin.browser.document fun HtmlBlockTag.include(component: Komponent) { - val consumer = this.consumer - if (consumer is KompConsumer) { - consumer.appendKomponent(component) - } + component.render(this.consumer as TagConsumer) } enum class UpdateStrategy { @@ -21,16 +20,15 @@ enum class UpdateStrategy { abstract class Komponent { var element: Node? = null - var kompElement: KompElement? = null val declaredStyles: MutableMap = HashMap() - open fun create(): KompElement { - val result = render(KompConsumer(this)) + open fun create(): HTMLElement { + val result = render(document.create) return result } - abstract fun render(consumer: KompConsumer): KompElement + abstract fun render(consumer: TagConsumer): HTMLElement open fun declareStyle(className: String, block: CSSStyleDeclaration.() -> Unit) { val style = (document.createElement("div") as HTMLDivElement).style @@ -46,20 +44,9 @@ abstract class Komponent { val newElement = create() - val replacedElement = if (updateStrategy == UpdateStrategy.REPLACE) { - //val replacedElement = replaceNode(newElement, element) + element.parentNode?.replaceChild(newElement, element) - replaceNode(newElement, element) - } else if (kompElement != null) { - kompElement?.let { - DomDiffer.replaceDiff(it, newElement, element) - } - } else { - newElement.create() - } - - kompElement = newElement - this.element = replacedElement + this.element = newElement } } @@ -67,22 +54,6 @@ abstract class Komponent { refresh() } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class.js != other::class.js) return false - - other as Komponent - - if (kompElement != other.kompElement) return false - - return true - } - - override fun hashCode(): Int { - var result = kompElement?.hashCode() ?: 0 - return result - } - companion object { var logRenderEvent = false @@ -90,25 +61,6 @@ abstract class Komponent { var logEquals = false var updateStrategy = UpdateStrategy.DOM_DIFF - fun replaceNode(newKomponent: KompElement, oldElement: Node): Node { - val newElement = newKomponent.create() - - if (logReplaceEvent) { - console.log("Replace", oldElement, newElement) - } - - val parent = oldElement.parentElement ?: throw IllegalStateException("oldElement has no parent! $oldElement") - - parent.replaceChild(newElement, oldElement) - - newKomponent.komponent?.also { - it.kompElement = newKomponent - it.element = newElement - } - - return newElement - } - fun removeElement(element: Node) { val parent = element.parentElement ?: throw IllegalArgumentException("Element has no parent!?") @@ -119,28 +71,16 @@ abstract class Komponent { parent.removeChild(element) } - fun appendElement(element: Node, kompElement: KompElement) { - val newElement = kompElement.create() - if (logReplaceEvent) { - console.log("Append", newElement) - } - element.appendChild(newElement) - } - fun create(parent: HTMLElement, component: Komponent, insertAsFirst: Boolean = false) { + val element = component.create() - component.kompElement = component.create() - val element = component.kompElement?.create() - - if (element != null) { - if (insertAsFirst && parent.childElementCount > 0) { - parent.insertBefore(element, parent.firstChild) - } else { - parent.appendChild(element) - } - - component.element = element + if (insertAsFirst && parent.childElementCount > 0) { + parent.insertBefore(element, parent.firstChild) + } else { + parent.appendChild(element) } + + component.element = element } } } diff --git a/src/main/kotlin/nl/astraeus/komp/SizedKomponent.kt b/src/main/kotlin/nl/astraeus/komp/SizedKomponent.kt deleted file mode 100644 index 20a3afe..0000000 --- a/src/main/kotlin/nl/astraeus/komp/SizedKomponent.kt +++ /dev/null @@ -1,55 +0,0 @@ -package nl.astraeus.komp - -import kotlinx.html.dom.create -import kotlinx.html.js.div -import kotlinx.html.style -import kotlin.browser.document - -/** - * User: rnentjes - * Date: 31-1-18 - * Time: 15:58 - */ - -enum class SizeType { - HBAR, - VBAR -} - -abstract class SizedKomponent( - val left: Int, - val top: Int, - val width: Int, - val height: Int -): Komponent() { - var parent: SizedKomponent? = null - var type: SizeType = SizeType.HBAR - var size: Int = 0 - - constructor( - parent: SizedKomponent, - type: SizeType, - size: Int - ) :this(0,0,0,0) { - this.parent = parent - this.type = type - this.size = size - } - - override fun create(): KompElement { - val innerResult = super.create() - - val result = document.create.div { - style = "left: ${left}px; top: ${top}px; width: ${width}px; height: ${height}px;" // sizing here - } - -/* - result.appendChild(innerResult) - this.element = result - return result -*/ - - return innerResult - } - -}