Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/commonMain/kotlin/api.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ interface TagConsumer<out R> {
fun onTagContentUnsafe(block: Unsafe.() -> Unit)
fun onTagComment(content: CharSequence)
fun finalize(): R
@ExperimentalKotlinxHtmlApi
val head: Any?
}

@RequiresOptIn
annotation class ExperimentalKotlinxHtmlApi

@HtmlTagMarker
interface Tag {
val tagName: String
Expand Down
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/delayed-consumer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class DelayedConsumer<T>(val downstream: TagConsumer<T>) : TagConsumer<T> {
return downstream.finalize()
}

@ExperimentalKotlinxHtmlApi
override val head: Any? get() = downstream.head

override fun onTagContentUnsafe(block: Unsafe.() -> Unit) {
processDelayedTag()
return downstream.onTagContentUnsafe(block)
Expand Down
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/filter-consumer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ private class FilterTagConsumer<T>(val downstream: TagConsumer<T>, val predicate
}

override fun finalize(): T = downstream.finalize()

@ExperimentalKotlinxHtmlApi
override val head: Any? get() = downstream.head
}

fun <T> TagConsumer<T>.filter(predicate: PredicateResults.(Tag) -> PredicateResult): TagConsumer<T> =
Expand Down
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/finalize-consumer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class FinalizeConsumer<F, T>(val downstream: TagConsumer<F>, val block: (F, Bool
override fun onTagComment(content: CharSequence) = downstream.onTagComment(content)

override fun finalize() = block(downstream.finalize(), level > 0)

@ExperimentalKotlinxHtmlApi
override val head: Any? get() = downstream.head
}

public fun <T> TagConsumer<T>.onFinalize(block: (from: T, partial: Boolean) -> Unit): TagConsumer<T> =
Expand Down
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/measure-consumer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ private class TimeMeasureConsumer<R>(val downstream: TagConsumer<R>, val start:
}

override fun finalize(): TimedValue<R> = TimedValue(downstream.finalize(), start.elapsedNow())

@ExperimentalKotlinxHtmlApi
override val head: Any? get() = downstream.head
}

fun <R> TagConsumer<R>.measureTime(timeSource: TimeSource = TimeSource.Monotonic): TagConsumer<TimedValue<R>> = TimeMeasureConsumer(this, timeSource.markNow())
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/stream.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class HTMLStreamBuilder<out O : Appendable>(

override fun finalize(): O = out

@ExperimentalKotlinxHtmlApi
override val head: O get() = out

override fun onTagContentUnsafe(block: Unsafe.() -> Unit) {
UnsafeImpl.block()
}
Expand Down
6 changes: 4 additions & 2 deletions src/jsMain/kotlin/dom-js.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kotlinx.html.dom

import kotlinx.html.DefaultUnsafe
import kotlinx.html.Entities
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.TagConsumer
import kotlinx.html.Unsafe
Expand Down Expand Up @@ -113,12 +114,13 @@ class JSDOMBuilder<out R : HTMLElement>(val document : Document) : TagConsumer<R

override fun finalize(): R = lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags")

@ExperimentalKotlinxHtmlApi
override val head: HTMLElement get() = path.last()

@Suppress("UnsafeCastFromDynamic")
private fun HTMLElement.asR(): R = this.asDynamic()

}


fun Document.createTree() : TagConsumer<HTMLElement> = JSDOMBuilder(this)
val Document.create : TagConsumer<HTMLElement>
get() = JSDOMBuilder(this)
Expand Down
32 changes: 32 additions & 0 deletions src/jsTest/kotlin/interoperable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import kotlinx.browser.document
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.js.div
import kotlinx.html.dom.append
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalKotlinxHtmlApi::class)
class InteroperableImplTest {
@Test fun testInteroperableDOM() {
val wrapper = wrapper()

wrapper.append.div {
interop {
document.createElement("svg")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a standin for what a sister library could do.

}
}

assertEquals(expected = "<div><svg></svg></div>", actual = wrapper.innerHTML)
}

private fun wrapper() = document.body!!.append.div {}

// Stand in for how a library might want to interop with kotlinx-html
private fun Tag.interop(block: () -> Node) {
val last = consumer.head as HTMLElement
last.appendChild(block())
}
}
4 changes: 4 additions & 0 deletions src/jvmMain/kotlin/dom-jvm.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kotlinx.html.dom

import kotlinx.html.Entities
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.TagConsumer
import kotlinx.html.Unsafe
Expand Down Expand Up @@ -100,6 +101,9 @@ class HTMLDOMBuilder(val document : Document) : TagConsumer<Element> {

override fun finalize() = lastLeaved ?: throw IllegalStateException("No tags were emitted")

@ExperimentalKotlinxHtmlApi
override val head: Element get() = path.last()

override fun onTagContentUnsafe(block: Unsafe.() -> Unit) {
UnsafeImpl.block()
}
Expand Down
30 changes: 30 additions & 0 deletions src/jvmTest/kotlin/interoperable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.div
import kotlinx.html.dom.createHTMLDocument
import kotlinx.html.dom.serialize
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import javax.xml.parsers.DocumentBuilderFactory
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalKotlinxHtmlApi::class)
class InteroperableImplTest {
@Test fun testInteroperableDOM() {
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
val tree = createHTMLDocument().div {
interop {
it.createElement("svg")
}
}
assertEquals(expected = "<!DOCTYPE html>\n<div><svg></svg></div>", actual = tree.serialize(prettyPrint = false))
}

// Stand in for how a library might want to interop with kotlinx-html
private fun Tag.interop(block: (Document) -> Node) {
val last = consumer.head as Element
last.appendChild(block(last.ownerDocument))
}
}
5 changes: 4 additions & 1 deletion src/wasmJsMain/kotlin/dom-js.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package kotlinx.html.dom

import kotlinx.html.DefaultUnsafe
import kotlinx.html.Entities
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.TagConsumer
import kotlinx.html.Unsafe
Expand Down Expand Up @@ -117,10 +118,12 @@ class JSDOMBuilder<out R : HTMLElement>(val document: Document) : TagConsumer<R>
override fun finalize(): R =
lastLeaved?.asR() ?: throw IllegalStateException("We can't finalize as there was no tags")

@ExperimentalKotlinxHtmlApi
override val head: Element get() = path.last()

private inline fun Element.asR(): R {
return jsCast(this)
}

}

fun <T : JsAny> jsCast(any: JsAny): T = js("(any)")
Expand Down
32 changes: 32 additions & 0 deletions src/wasmJsTest/kotlin/interoperable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import kotlinx.browser.document
import kotlinx.html.ExperimentalKotlinxHtmlApi
import kotlinx.html.Tag
import kotlinx.html.js.div
import kotlinx.html.dom.append
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalKotlinxHtmlApi::class)
class InteroperableImplTest {
@Test fun testInteroperableDOM() {
val wrapper = wrapper()

wrapper.append.div {
interop {
document.createElement("svg")
}
}

assertEquals(expected = "<div><svg></svg></div>", actual = wrapper.innerHTML)
}

private fun wrapper() = document.body!!.append.div {}

// Stand in for how a library might want to interop with kotlinx-html
private fun Tag.interop(block: () -> Node) {
val last = consumer.head as HTMLElement
last.appendChild(block())
}
}
Loading