refactor compose-html

This commit is contained in:
Alexander Nozik 2024-02-12 20:04:10 +03:00
parent 38d6a9c419
commit d90c1edc6c
21 changed files with 144 additions and 89 deletions

View File

@ -30,7 +30,6 @@ kscience {
commonMain { commonMain {
implementation(projects.visionforgeSolid) implementation(projects.visionforgeSolid)
implementation(projects.visionforgeComposeHtml)
} }
jvmMain { jvmMain {
implementation("org.apache.commons:commons-math3:3.6.1") implementation("org.apache.commons:commons-math3:3.6.1")
@ -40,12 +39,15 @@ kscience {
implementation("ch.qos.logback:logback-classic:1.2.11") implementation("ch.qos.logback:logback-classic:1.2.11")
} }
jsMain { jsMain {
// implementation(projects.visionforgeComposeHtml)
implementation(projects.visionforgeThreejs) implementation(projects.visionforgeThreejs)
//implementation(devNpm("webpack-bundle-analyzer", "4.4.0")) //implementation(devNpm("webpack-bundle-analyzer", "4.4.0"))
} }
} }
kotlin{
explicitApi = null
}
kotlin.explicitApi = null
application { application {
mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt") mainClass.set("ru.mipt.npm.muon.monitor.server.MMServerKt")

View File

@ -4,9 +4,8 @@ plugins {
} }
kscience { kscience {
jvm()
js() js()
// wasm() jvm()
} }
kotlin { kotlin {
@ -15,23 +14,22 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
api(projects.visionforgeCore) api(projects.visionforgeCore)
}
}
jvmMain{
//need this to placate compose compiler in MPP applications
dependencies{
api(compose.runtime) api(compose.runtime)
} }
} }
val jvmMain by getting { jsMain{
dependencies { dependencies {
api(compose.foundation)
api(compose.material)
api(compose.preview)
}
}
val jsMain by getting {
dependencies {
api(compose.html.core)
api("app.softwork:bootstrap-compose:0.1.15") api("app.softwork:bootstrap-compose:0.1.15")
api("app.softwork:bootstrap-compose-icons:0.1.15") api("app.softwork:bootstrap-compose-icons:0.1.15")
api(compose.runtime)
api(compose.html.core)
} }
} }
} }

View File

@ -9,20 +9,19 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.ElementVisionRenderer import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
/** /**
* An [ElementVisionRenderer] that could be used directly in Compose-html as well as a stand-alone renderer * An [ElementVisionRenderer] that could be used directly in Compose-html as well as a stand-alone renderer
*/ */
public interface ComposeVisionRenderer: ElementVisionRenderer { public interface ComposeHtmlVisionRenderer : ElementVisionRenderer {
@Composable @Composable
public fun DOMScope<Element>.render(client: VisionClient, name: Name, vision: Vision, meta: Meta) public fun DOMScope<Element>.render(name: Name, vision: Vision, meta: Meta)
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
renderComposable(element) { renderComposable(element) {
Style(VisionForgeStyles) Style(VisionForgeStyles)
render(client, name, vision, meta) render(name, vision, meta)
} }
} }

View File

@ -48,7 +48,7 @@ public fun Vision(
} }
DisposableEffect(vision, name, renderer, meta) { DisposableEffect(vision, name, renderer, meta) {
renderer.render(scopeElement, client, name, vision, meta) renderer.render(scopeElement, name, vision, meta)
onDispose { onDispose {
scopeElement.clear() scopeElement.clear()
} }

View File

@ -5,7 +5,7 @@ plugins {
kscience { kscience {
jvm() jvm()
wasm() // wasm()
} }
kotlin { kotlin {
@ -16,6 +16,10 @@ kotlin {
api(projects.visionforgeCore) api(projects.visionforgeCore)
api(compose.runtime) api(compose.runtime)
api(compose.foundation) api(compose.foundation)
}
}
jvmMain{
dependencies{
api(compose.material) api(compose.material)
api(compose.preview) api(compose.preview)
} }

View File

@ -0,0 +1,15 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.Composable
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
public interface ComposeVisionRenderer {
public fun rateVision(vision: Vision): Int
@Composable
public fun render(client: VisionClient, name: Name, vision: Vision, meta: Meta)
public companion object
}

View File

@ -0,0 +1,24 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.visionforge.Vision
/**
* Render an Element vision via injected vision renderer inside compose-html
*/
@Composable
public fun Vision(
context: Context,
vision: Vision,
name: Name = "@vision[${vision.hashCode().toString(16)}]".asName(),
meta: Meta = Meta.EMPTY,
modifier: Modifier = Modifier,
) {
}

View File

@ -8,7 +8,7 @@ kscience {
jvm() jvm()
js() js()
native() native()
// wasm() wasm()
useCoroutines() useCoroutines()
commonMain { commonMain {
api("space.kscience:dataforge-context:$dataforgeVersion") api("space.kscience:dataforge-context:$dataforgeVersion")

View File

@ -2,12 +2,11 @@ package space.kscience.visionforge
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.parseAsName import space.kscience.dataforge.names.parseAsName
@ -23,6 +22,9 @@ public abstract class VisionControlEvent : VisionEvent, MetaRepr {
public interface ControlVision : Vision { public interface ControlVision : Vision {
public val controlEventFlow: SharedFlow<VisionControlEvent> public val controlEventFlow: SharedFlow<VisionControlEvent>
/**
* Fire a [VisionControlEvent] on this [ControlVision]
*/
public suspend fun dispatchControlEvent(event: VisionControlEvent) public suspend fun dispatchControlEvent(event: VisionControlEvent)
override suspend fun receiveEvent(event: VisionEvent) { override suspend fun receiveEvent(event: VisionEvent) {
@ -32,6 +34,28 @@ public interface ControlVision : Vision {
} }
} }
public fun ControlVision.asyncControlEvent(
event: VisionControlEvent,
scope: CoroutineScope = manager?.context ?: error("Can't fire asynchronous event for an orphan vision. Provide a scope."),
) {
scope.launch { dispatchControlEvent(event) }
}
@Serializable
public abstract class AbstractControlVision : AbstractVision(), ControlVision {
@Transient
private val mutableControlEventFlow = MutableSharedFlow<VisionControlEvent>()
override val controlEventFlow: SharedFlow<VisionControlEvent>
get() = mutableControlEventFlow
override suspend fun dispatchControlEvent(event: VisionControlEvent) {
mutableControlEventFlow.emit(event)
}
}
/** /**
* An event for submitting changes * An event for submitting changes

View File

@ -2,21 +2,21 @@ package space.kscience.visionforge.html
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.html.DIV import kotlinx.html.DIV
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.visionforge.* import space.kscience.visionforge.*
@Serializable public interface VisionOfHtml : Vision {
public abstract class VisionOfHtml : AbstractVision() {
public var classes: Set<String> public var classes: Set<String>
get() = properties[::classes.name, false].stringList?.toSet() ?: emptySet() get() = properties[::classes.name, false].stringList?.toSet() ?: emptySet()
set(value) { set(value) {
@ -26,7 +26,7 @@ public abstract class VisionOfHtml : AbstractVision() {
@Serializable @Serializable
@SerialName("html.plain") @SerialName("html.plain")
public class VisionOfPlainHtml : VisionOfHtml() { public class VisionOfPlainHtml : AbstractVision(), VisionOfHtml {
public var content: String? by properties.string() public var content: String? by properties.string()
} }
@ -59,26 +59,11 @@ public enum class InputFeedbackMode {
NONE NONE
} }
@Serializable
public abstract class VisionOfHtmlControl: VisionOfHtml(), ControlVision{
@Transient
private val mutableControlEventFlow = MutableSharedFlow<VisionControlEvent>()
override val controlEventFlow: SharedFlow<VisionControlEvent>
get() = mutableControlEventFlow
override suspend fun dispatchControlEvent(event: VisionControlEvent) {
mutableControlEventFlow.emit(event)
}
}
@Serializable @Serializable
@SerialName("html.input") @SerialName("html.input")
public open class VisionOfHtmlInput( public open class VisionOfHtmlInput(
public val inputType: String, public val inputType: String,
) : VisionOfHtmlControl() { ) : AbstractControlVision(), VisionOfHtml {
public var value: Value? by properties.value() public var value: Value? by properties.value()
public var disabled: Boolean by properties.boolean { false } public var disabled: Boolean by properties.boolean { false }
public var fieldName: String? by properties.string() public var fieldName: String? by properties.string()

View File

@ -8,6 +8,7 @@ import kotlinx.serialization.Serializable
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.node import space.kscience.dataforge.meta.node
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.visionforge.AbstractControlVision
import space.kscience.visionforge.DataControl import space.kscience.visionforge.DataControl
import space.kscience.visionforge.onSubmit import space.kscience.visionforge.onSubmit
@ -18,7 +19,7 @@ import space.kscience.visionforge.onSubmit
@SerialName("html.form") @SerialName("html.form")
public class VisionOfHtmlForm( public class VisionOfHtmlForm(
public val formId: String, public val formId: String,
) : VisionOfHtmlControl(), DataControl { ) : AbstractControlVision(), DataControl, VisionOfHtml {
public var values: Meta? by properties.node() public var values: Meta? by properties.node()
} }
@ -45,7 +46,7 @@ public fun VisionOfHtmlForm.onFormSubmit(scope: CoroutineScope, block: (Meta?) -
@Serializable @Serializable
@SerialName("html.button") @SerialName("html.button")
public class VisionOfHtmlButton : VisionOfHtmlControl(), DataControl { public class VisionOfHtmlButton : AbstractControlVision(), DataControl, VisionOfHtml {
public var label: String? by properties.string() public var label: String? by properties.string()
} }

View File

@ -30,7 +30,6 @@ public interface ElementVisionRenderer {
*/ */
public fun render( public fun render(
element: Element, element: Element,
client: VisionClient,
name: Name, name: Name,
vision: Vision, vision: Vision,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
@ -49,7 +48,7 @@ public interface ElementVisionRenderer {
public class SingleTypeVisionRenderer<T : Vision>( public class SingleTypeVisionRenderer<T : Vision>(
public val kClass: KClass<T>, public val kClass: KClass<T>,
private val acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING, private val acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
private val renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit, private val renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
) : ElementVisionRenderer { ) : ElementVisionRenderer {
override fun rateVision(vision: Vision): Int = override fun rateVision(vision: Vision): Int =
@ -57,19 +56,18 @@ public class SingleTypeVisionRenderer<T : Vision>(
override fun render( override fun render(
element: Element, element: Element,
client: VisionClient,
name: Name, name: Name,
vision: Vision, vision: Vision,
meta: Meta, meta: Meta,
) { ) {
element.clear() element.clear()
element.append { element.append {
renderFunction(name, client, kClass.cast(vision), meta) renderFunction(name, kClass.cast(vision), meta)
} }
} }
} }
public inline fun <reified T : Vision> ElementVisionRenderer( public inline fun <reified T : Vision> ElementVisionRenderer(
acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING, acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit, noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction) ): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)

View File

@ -96,7 +96,13 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
vision.setAsRoot(visionManager) vision.setAsRoot(visionManager)
val renderer: ElementVisionRenderer = val renderer: ElementVisionRenderer =
findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}") findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
renderer.render(element, this, name, vision, outputMeta) //subscribe to a backwards events propagation for control visions
if(vision is ControlVision){
vision.controlEventFlow.onEach {
sendEvent(name,it)
}.launchIn(context)
}
renderer.render(element, name, vision, outputMeta)
} }
private fun startVisionUpdate(element: Element, visionName: Name, vision: Vision, outputMeta: Meta) { private fun startVisionUpdate(element: Element, visionName: Name, vision: Vision, outputMeta: Meta) {

View File

@ -49,7 +49,7 @@ public fun VisionClient.sendMetaEvent(targetName: Name, payload: MetaRepr): Unit
} }
internal val formVisionRenderer: ElementVisionRenderer = internal val formVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfHtmlForm> { name, client, vision, _ -> ElementVisionRenderer<VisionOfHtmlForm> { name, vision, _ ->
val form = document.getElementById(vision.formId) as? HTMLFormElement val form = document.getElementById(vision.formId) as? HTMLFormElement
?: error("An element with id = '${vision.formId} is not a form") ?: error("An element with id = '${vision.formId} is not a form")
@ -69,22 +69,18 @@ internal val formVisionRenderer: ElementVisionRenderer =
form.onsubmit = { event -> form.onsubmit = { event ->
event.preventDefault() event.preventDefault()
val formData = FormData(form).toMeta() val formData = FormData(form).toMeta()
client.context.launch { vision.asyncControlEvent(VisionSubmitEvent(name = name, payload = formData))
client.sendEvent(name, VisionSubmitEvent(name = name, payload = formData))
}
console.info("Sent form data: ${formData.toMap()}") console.info("Sent form data: ${formData.toMap()}")
false false
} }
} }
internal val buttonVisionRenderer: ElementVisionRenderer = internal val buttonVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfHtmlButton> { name, client, vision, _ -> ElementVisionRenderer<VisionOfHtmlButton> { name, vision, _ ->
button(type = ButtonType.button).also { button -> button(type = ButtonType.button).also { button ->
button.subscribeToVision(vision) button.subscribeToVision(vision)
button.onclick = { button.onclick = {
client.context.launch { vision.asyncControlEvent(VisionSubmitEvent(name = name))
client.sendEvent(name, VisionSubmitEvent(name = name))
}
} }
vision.useProperty(VisionOfHtmlButton::label) { vision.useProperty(VisionOfHtmlButton::label) {
button.innerHTML = it ?: "" button.innerHTML = it ?: ""

View File

@ -35,7 +35,7 @@ private fun HTMLInputElement.subscribeToInput(inputVision: VisionOfHtmlInput) {
} }
internal val htmlVisionRenderer: ElementVisionRenderer = internal val htmlVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfPlainHtml> { _, _, vision, _ -> ElementVisionRenderer<VisionOfPlainHtml> { _, vision, _ ->
div().also { div -> div().also { div ->
div.subscribeToVision(vision) div.subscribeToVision(vision)
vision.useProperty(VisionOfPlainHtml::content) { vision.useProperty(VisionOfPlainHtml::content) {
@ -47,17 +47,18 @@ internal val htmlVisionRenderer: ElementVisionRenderer =
internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlInput>( internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlInput>(
acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1 acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1
) { name, client, vision, _ -> ) { name, vision, _ ->
input { input {
type = InputType.text type = InputType.text
}.also { htmlInputElement -> }.also { htmlInputElement ->
htmlInputElement.onchange = { htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent( VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = { htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent(VisionInputEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
@ -68,17 +69,17 @@ internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<
} }
internal val checkboxVisionRenderer: ElementVisionRenderer = internal val checkboxVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfCheckbox> { name, client, vision, _ -> ElementVisionRenderer<VisionOfCheckbox> { name, vision, _ ->
input { input {
type = InputType.checkBox type = InputType.checkBox
}.also { htmlInputElement -> }.also { htmlInputElement ->
htmlInputElement.onchange = { htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent(VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = { htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent(VisionInputEvent(htmlInputElement.value.asValue(), name))
} }
@ -90,17 +91,17 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
} }
internal val textVisionRenderer: ElementVisionRenderer = internal val textVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfTextField> { name, client, vision, _ -> ElementVisionRenderer<VisionOfTextField> { name, vision, _ ->
input { input {
type = InputType.text type = InputType.text
}.also { htmlInputElement -> }.also { htmlInputElement ->
htmlInputElement.onchange = { htmlInputElement.onchange = {
client.sendEventAsync(name, VisionValueChangeEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent(VisionValueChangeEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.oninput = { htmlInputElement.oninput = {
client.sendEventAsync(name, VisionInputEvent(htmlInputElement.value.asValue(), name)) vision.asyncControlEvent(VisionInputEvent(htmlInputElement.value.asValue(), name))
} }
htmlInputElement.subscribeToInput(vision) htmlInputElement.subscribeToInput(vision)
@ -111,20 +112,20 @@ internal val textVisionRenderer: ElementVisionRenderer =
} }
internal val numberVisionRenderer: ElementVisionRenderer = internal val numberVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfNumberField> { name, client, vision, _ -> ElementVisionRenderer<VisionOfNumberField> { name, vision, _ ->
input { input {
type = InputType.number type = InputType.number
}.also { htmlInputElement -> }.also { htmlInputElement ->
htmlInputElement.onchange = { htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name)) vision.asyncControlEvent(VisionValueChangeEvent(it.asValue(), name))
} }
} }
htmlInputElement.oninput = { htmlInputElement.oninput = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name)) vision.asyncControlEvent(VisionInputEvent(it.asValue(), name))
} }
} }
@ -137,7 +138,7 @@ internal val numberVisionRenderer: ElementVisionRenderer =
} }
internal val rangeVisionRenderer: ElementVisionRenderer = internal val rangeVisionRenderer: ElementVisionRenderer =
ElementVisionRenderer<VisionOfRangeField> { name, client, vision, _ -> ElementVisionRenderer<VisionOfRangeField> { name, vision, _ ->
input { input {
type = InputType.range type = InputType.range
min = vision.min.toString() min = vision.min.toString()
@ -147,13 +148,13 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
htmlInputElement.onchange = { htmlInputElement.onchange = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionValueChangeEvent(it.asValue(), name)) vision.asyncControlEvent(VisionValueChangeEvent(it.asValue(), name))
} }
} }
htmlInputElement.oninput = { htmlInputElement.oninput = {
htmlInputElement.value.toDoubleOrNull()?.let { htmlInputElement.value.toDoubleOrNull()?.let {
client.sendEventAsync(name, VisionInputEvent(it.asValue(), name)) vision.asyncControlEvent(VisionInputEvent(it.asValue(), name))
} }
} }

View File

@ -27,7 +27,7 @@ public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
else -> ElementVisionRenderer.ZERO_RATING else -> ElementVisionRenderer.ZERO_RATING
} }
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element,name: Name, vision: Vision, meta: Meta) {
require(vision is VisionOfMarkup) { "The vision is not a markup vision" } require(vision is VisionOfMarkup) { "The vision is not a markup vision" }
val div = document.createElement("div") val div = document.createElement("div")
val flavour = when (vision.format) { val flavour = when (vision.format) {

View File

@ -10,7 +10,10 @@ import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import space.kscience.plotly.PlotlyConfig import space.kscience.plotly.PlotlyConfig
import space.kscience.plotly.plot import space.kscience.plotly.plot
import space.kscience.visionforge.* import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionPlugin
public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer { public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
public val visionClient: JsVisionClient by require(JsVisionClient) public val visionClient: JsVisionClient by require(JsVisionClient)
@ -24,7 +27,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
else -> ElementVisionRenderer.ZERO_RATING else -> ElementVisionRenderer.ZERO_RATING
} }
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found") val plot = (vision as? VisionOfPlotly)?.plot ?: error("VisionOfPlotly expected but ${vision::class} found")
val config = PlotlyConfig.read(meta) val config = PlotlyConfig.read(meta)
element.plot(config, plot) element.plot(config, plot)

View File

@ -14,7 +14,6 @@ import space.kscience.dataforge.names.asName
import space.kscience.visionforge.ElementVisionRenderer import space.kscience.visionforge.ElementVisionRenderer
import space.kscience.visionforge.JsVisionClient import space.kscience.visionforge.JsVisionClient
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionClient
import tabulator.Tabulator import tabulator.Tabulator
import tabulator.TabulatorFull import tabulator.TabulatorFull
@ -35,7 +34,7 @@ public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
else -> ElementVisionRenderer.ZERO_RATING else -> ElementVisionRenderer.ZERO_RATING
} }
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
val table: VisionOfTable = (vision as? VisionOfTable) val table: VisionOfTable = (vision as? VisionOfTable)
?: error("VisionOfTable expected but ${vision::class} found") ?: error("VisionOfTable expected but ${vision::class} found")

View File

@ -14,10 +14,10 @@ kscience {
commonMain { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeComposeHtml)
} }
jsMain { jsMain {
api(projects.visionforgeComposeHtml)
implementation(npm("three", "0.143.0")) implementation(npm("three", "0.143.0"))
implementation(npm("three-csg-ts", "3.1.13")) implementation(npm("three-csg-ts", "3.1.13"))
implementation(npm("three.meshline", "1.4.0")) implementation(npm("three.meshline", "1.4.0"))

View File

@ -10,7 +10,7 @@ import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
import space.kscience.visionforge.html.ComposeVisionRenderer import space.kscience.visionforge.html.ComposeHtmlVisionRenderer
import space.kscience.visionforge.solid.* import space.kscience.visionforge.solid.*
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import space.kscience.visionforge.solid.three.compose.ThreeView import space.kscience.visionforge.solid.three.compose.ThreeView
@ -22,7 +22,7 @@ import three.objects.Group as ThreeGroup
/** /**
* A plugin that handles Three Object3D representation of Visions. * A plugin that handles Three Object3D representation of Visions.
*/ */
public class ThreePlugin : AbstractPlugin(), ComposeVisionRenderer { public class ThreePlugin : AbstractPlugin(), ComposeHtmlVisionRenderer {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
public val solids: Solids by require(Solids) public val solids: Solids by require(Solids)
@ -186,7 +186,7 @@ public class ThreePlugin : AbstractPlugin(), ComposeVisionRenderer {
} }
@Composable @Composable
override fun DOMScope<Element>.render(client: VisionClient, name: Name, vision: Vision, meta: Meta) { override fun DOMScope<Element>.render(name: Name, vision: Vision, meta: Meta) {
require(vision is Solid) { "Expected Solid but found ${vision::class}" } require(vision is Solid) { "Expected Solid but found ${vision::class}" }
ThreeView(context, vision, null, Canvas3DOptions.read(meta)) ThreeView(context, vision, null, Canvas3DOptions.read(meta))
} }

View File

@ -1,3 +1,4 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose) alias(spclibs.plugins.compose)
@ -22,7 +23,6 @@ kscience {
commonMain { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeComposeHtml)
} }
jvmMain { jvmMain {