Compare commits
3 Commits
9fc6f1e34c
...
f40bed7bb9
Author | SHA1 | Date | |
---|---|---|---|
f40bed7bb9 | |||
05b87857f4 | |||
bce61c0fb0 |
build.gradle.kts
demo/playground/src/jvmMain/kotlin
ui/ring/src/jsMain/kotlin/space.kscience.visionforge.ring
visionforge-core/src
commonMain/kotlin/space/kscience/visionforge
jsMain/kotlin/space/kscience/visionforge
visionforge-markdown/src/jsMain/kotlin/space/kscience/visionforge/markup
visionforge-plotly/src/jsMain/kotlin/space/kscience/visionforge/plotly
visionforge-tables/src/jsMain/kotlin/space/kscience/visionforge/tables
visionforge-threejs/src/jsMain/kotlin/space/kscience/visionforge/solid/three
@ -12,7 +12,7 @@ val fxVersion by extra("11")
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "space.kscience"
|
group = "space.kscience"
|
||||||
version = "0.3.0-dev-17"
|
version = "0.3.0-dev-18"
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
|
59
demo/playground/src/jvmMain/kotlin/controlVision.kt
Normal file
59
demo/playground/src/jvmMain/kotlin/controlVision.kt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package space.kscience.visionforge.examples
|
||||||
|
|
||||||
|
import kotlinx.html.p
|
||||||
|
import space.kscience.visionforge.VisionControlEvent
|
||||||
|
import space.kscience.visionforge.html.*
|
||||||
|
import space.kscience.visionforge.onClick
|
||||||
|
|
||||||
|
|
||||||
|
fun main() = serve {
|
||||||
|
|
||||||
|
val events = ArrayDeque<VisionControlEvent>(10)
|
||||||
|
|
||||||
|
val html = VisionOfPlainHtml()
|
||||||
|
|
||||||
|
fun pushEvent(event: VisionControlEvent) {
|
||||||
|
events.addFirst(event)
|
||||||
|
if (events.size >= 10) {
|
||||||
|
events.removeLast()
|
||||||
|
}
|
||||||
|
html.content {
|
||||||
|
events.forEach { event ->
|
||||||
|
p {
|
||||||
|
text(event.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vision {
|
||||||
|
htmlCheckBox {
|
||||||
|
checked = true
|
||||||
|
onValueChange(context) {
|
||||||
|
pushEvent(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vision {
|
||||||
|
htmlRangeField(1, 10) {
|
||||||
|
numberValue = 4
|
||||||
|
onValueChange(context) {
|
||||||
|
pushEvent(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vision {
|
||||||
|
button("Click me"){
|
||||||
|
onClick(context){
|
||||||
|
pushEvent(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vision(html)
|
||||||
|
}
|
@ -13,6 +13,7 @@ import space.kscience.dataforge.names.Name
|
|||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
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
|
||||||
import space.kscience.visionforge.react.render
|
import space.kscience.visionforge.react.render
|
||||||
import space.kscience.visionforge.solid.Solid
|
import space.kscience.visionforge.solid.Solid
|
||||||
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
|
||||||
@ -26,9 +27,9 @@ public class ThreeWithControlsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
override fun rateVision(vision: Vision): Int =
|
override fun rateVision(vision: Vision): Int =
|
||||||
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
if (vision is Solid) ElementVisionRenderer.DEFAULT_RATING * 2 else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) {
|
||||||
if(meta["controls.enabled"].boolean == false){
|
if (meta["controls.enabled"].boolean == false) {
|
||||||
three.render(element, name, vision, meta)
|
three.render(element, client, name, vision, meta)
|
||||||
} else {
|
} else {
|
||||||
space.kscience.visionforge.react.createRoot(element).render {
|
space.kscience.visionforge.react.createRoot(element).render {
|
||||||
child(ThreeCanvasWithControls) {
|
child(ThreeCanvasWithControls) {
|
||||||
|
@ -8,13 +8,13 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.MetaRepr
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.meta.MutableMeta
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("control")
|
@SerialName("control")
|
||||||
public abstract class VisionControlEvent : VisionEvent, MetaRepr {
|
public sealed class VisionControlEvent : VisionEvent, MetaRepr {
|
||||||
public abstract val meta: Meta
|
public abstract val meta: Meta
|
||||||
|
|
||||||
override fun toMeta(): Meta = meta
|
override fun toMeta(): Meta = meta
|
||||||
@ -23,30 +23,41 @@ public abstract class VisionControlEvent : VisionEvent, MetaRepr {
|
|||||||
public interface ControlVision : Vision {
|
public interface ControlVision : Vision {
|
||||||
public val controlEventFlow: SharedFlow<VisionControlEvent>
|
public val controlEventFlow: SharedFlow<VisionControlEvent>
|
||||||
|
|
||||||
public fun dispatchControlEvent(event: VisionControlEvent)
|
public suspend fun dispatchControlEvent(event: VisionControlEvent)
|
||||||
|
|
||||||
override fun receiveEvent(event: VisionEvent) {
|
override suspend fun receiveEvent(event: VisionEvent) {
|
||||||
if (event is VisionControlEvent) {
|
if (event is VisionControlEvent) {
|
||||||
dispatchControlEvent(event)
|
dispatchControlEvent(event)
|
||||||
} else super.receiveEvent(event)
|
} else super.receiveEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param payload The optional payload associated with the click event.
|
* @param payload The optional payload associated with the click event.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("control.click")
|
@SerialName("control.click")
|
||||||
public class VisionClickEvent(public val payload: Meta = Meta.EMPTY) : VisionControlEvent() {
|
public class VisionClickEvent(override val meta: Meta) : VisionControlEvent() {
|
||||||
override val meta: Meta get() = Meta { ::payload.name put payload }
|
public val payload: Meta? by meta.node()
|
||||||
|
public val name: Name? get() = meta["name"].string?.parseAsName()
|
||||||
|
|
||||||
|
override fun toString(): String = meta.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun VisionClickEvent(payload: Meta = Meta.EMPTY, name: Name? = null): VisionClickEvent = VisionClickEvent(
|
||||||
|
Meta {
|
||||||
|
VisionClickEvent::payload.name put payload
|
||||||
|
VisionClickEvent::name.name put name.toString()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
public interface ClickControl : ControlVision {
|
public interface ClickControl : ControlVision {
|
||||||
/**
|
/**
|
||||||
* Create and dispatch a click event
|
* Create and dispatch a click event
|
||||||
*/
|
*/
|
||||||
public fun click(builder: MutableMeta.() -> Unit = {}) {
|
public suspend fun click(builder: MutableMeta.() -> Unit = {}) {
|
||||||
dispatchControlEvent(VisionClickEvent(Meta(builder)))
|
dispatchControlEvent(VisionClickEvent(Meta(builder)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,4 +71,21 @@ public fun ClickControl.onClick(scope: CoroutineScope, block: suspend VisionClic
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("control.valueChange")
|
@SerialName("control.valueChange")
|
||||||
public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEvent()
|
public class VisionValueChangeEvent(override val meta: Meta) : VisionControlEvent() {
|
||||||
|
|
||||||
|
public val value: Value? get() = meta.value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of a control that fired the event
|
||||||
|
*/
|
||||||
|
public val name: Name? get() = meta["name"]?.string?.parseAsName()
|
||||||
|
|
||||||
|
override fun toString(): String = meta.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun VisionValueChangeEvent(value: Value?, name: Name? = null): VisionValueChangeEvent = VisionValueChangeEvent(
|
||||||
|
Meta {
|
||||||
|
this.value = value
|
||||||
|
name?.let { set("name", it.toString()) }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -50,7 +50,7 @@ public interface Vision : Described {
|
|||||||
/**
|
/**
|
||||||
* Receive and process a generic [VisionEvent].
|
* Receive and process a generic [VisionEvent].
|
||||||
*/
|
*/
|
||||||
public fun receiveEvent(event: VisionEvent) {
|
public suspend fun receiveEvent(event: VisionEvent) {
|
||||||
if(event is VisionChange) update(event)
|
if(event is VisionChange) update(event)
|
||||||
else manager?.logger?.warn { "Undispatched event: $event" }
|
else manager?.logger?.warn { "Undispatched event: $event" }
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import space.kscience.dataforge.context.Plugin
|
import space.kscience.dataforge.context.Plugin
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaRepr
|
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.parseAsName
|
import space.kscience.dataforge.names.parseAsName
|
||||||
|
|
||||||
@ -33,9 +31,3 @@ public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: St
|
|||||||
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
|
public fun VisionClient.notifyPropertyChanged(visionName: Name, propertyName: String, item: Boolean) {
|
||||||
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
notifyPropertyChanged(visionName, propertyName.parseAsName(true), Meta(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun VisionClient.sendEvent(targetName: Name, payload: MetaRepr): Unit {
|
|
||||||
context.launch {
|
|
||||||
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
|
|
||||||
}
|
|
||||||
}
|
|
@ -69,12 +69,14 @@ public class VisionManager(meta: Meta) : AbstractPlugin(meta), MutableVisionCont
|
|||||||
defaultDeserializer { SimpleVisionGroup.serializer() }
|
defaultDeserializer { SimpleVisionGroup.serializer() }
|
||||||
subclass(NullVision.serializer())
|
subclass(NullVision.serializer())
|
||||||
subclass(SimpleVisionGroup.serializer())
|
subclass(SimpleVisionGroup.serializer())
|
||||||
|
subclass(VisionOfPlainHtml.serializer())
|
||||||
subclass(VisionOfHtmlInput.serializer())
|
subclass(VisionOfHtmlInput.serializer())
|
||||||
subclass(VisionOfNumberField.serializer())
|
subclass(VisionOfNumberField.serializer())
|
||||||
subclass(VisionOfTextField.serializer())
|
subclass(VisionOfTextField.serializer())
|
||||||
subclass(VisionOfCheckbox.serializer())
|
subclass(VisionOfCheckbox.serializer())
|
||||||
subclass(VisionOfRangeField.serializer())
|
subclass(VisionOfRangeField.serializer())
|
||||||
subclass(VisionOfHtmlForm.serializer())
|
subclass(VisionOfHtmlForm.serializer())
|
||||||
|
subclass(VisionOfHtmlButton.serializer())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,21 @@
|
|||||||
package space.kscience.visionforge.html
|
package space.kscience.visionforge.html
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.html.DIV
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.TagConsumer
|
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.AbstractVision
|
import space.kscience.visionforge.AbstractVision
|
||||||
|
import space.kscience.visionforge.ControlVision
|
||||||
|
import space.kscience.visionforge.VisionControlEvent
|
||||||
|
import space.kscience.visionforge.VisionValueChangeEvent
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -21,8 +29,10 @@ public class VisionOfPlainHtml : VisionOfHtml() {
|
|||||||
public var content: String? by properties.string()
|
public var content: String? by properties.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline fun VisionOfPlainHtml.content(block: TagConsumer<*>.() -> Unit) {
|
public fun VisionOfPlainHtml.content(block: DIV.() -> Unit) {
|
||||||
content = createHTML().apply(block).finalize()
|
content = createHTML().apply {
|
||||||
|
div(block = block)
|
||||||
|
}.finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
@Suppress("UnusedReceiverParameter")
|
||||||
@ -48,17 +58,40 @@ public enum class InputFeedbackMode {
|
|||||||
NONE
|
NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
public val feedbackMode: InputFeedbackMode = InputFeedbackMode.ONCHANGE,
|
||||||
) : VisionOfHtml() {
|
) : VisionOfHtmlControl() {
|
||||||
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()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger [callback] on each value change
|
||||||
|
*/
|
||||||
|
public fun VisionOfHtmlInput.onValueChange(
|
||||||
|
scope: CoroutineScope = manager?.context ?: error("Coroutine context is not resolved for $this"),
|
||||||
|
callback: suspend VisionValueChangeEvent.() -> Unit,
|
||||||
|
): Job = controlEventFlow.filterIsInstance<VisionValueChangeEvent>().onEach(callback).launchIn(scope)
|
||||||
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
@Suppress("UnusedReceiverParameter")
|
||||||
public inline fun VisionOutput.htmlInput(
|
public inline fun VisionOutput.htmlInput(
|
||||||
inputType: String,
|
inputType: String,
|
||||||
@ -91,7 +124,7 @@ public inline fun VisionOutput.htmlCheckBox(
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("html.number")
|
@SerialName("html.number")
|
||||||
public class VisionOfNumberField : VisionOfHtmlInput(InputType.number.realValue) {
|
public class VisionOfNumberField : VisionOfHtmlInput(InputType.number.realValue) {
|
||||||
public var number: Number? by properties.number(key = VisionOfHtmlInput::value.name.asName())
|
public var numberValue: Number? by properties.number(key = VisionOfHtmlInput::value.name.asName())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
@Suppress("UnusedReceiverParameter")
|
||||||
@ -106,14 +139,14 @@ public class VisionOfRangeField(
|
|||||||
public val max: Double,
|
public val max: Double,
|
||||||
public val step: Double = 1.0,
|
public val step: Double = 1.0,
|
||||||
) : VisionOfHtmlInput(InputType.range.realValue) {
|
) : VisionOfHtmlInput(InputType.range.realValue) {
|
||||||
public var number: Number? by properties.number(key = VisionOfHtmlInput::value.name.asName())
|
public var numberValue: Number? by properties.number(key = VisionOfHtmlInput::value.name.asName())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedReceiverParameter")
|
@Suppress("UnusedReceiverParameter")
|
||||||
public inline fun VisionOutput.htmlRangeField(
|
public inline fun VisionOutput.htmlRangeField(
|
||||||
min: Double,
|
min: Number,
|
||||||
max: Double,
|
max: Number,
|
||||||
step: Double = 1.0,
|
step: Number = 1.0,
|
||||||
block: VisionOfRangeField.() -> Unit = {},
|
block: VisionOfRangeField.() -> Unit = {},
|
||||||
): VisionOfRangeField = VisionOfRangeField(min, max, step).apply(block)
|
): VisionOfRangeField = VisionOfRangeField(min.toDouble(), max.toDouble(), step.toDouble()).apply(block)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
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.visionforge.ClickControl
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param formId an id of the element in rendered DOM, this form is bound to
|
* @param formId an id of the element in rendered DOM, this form is bound to
|
||||||
@ -16,7 +18,7 @@ import space.kscience.dataforge.meta.node
|
|||||||
@SerialName("html.form")
|
@SerialName("html.form")
|
||||||
public class VisionOfHtmlForm(
|
public class VisionOfHtmlForm(
|
||||||
public val formId: String,
|
public val formId: String,
|
||||||
) : VisionOfHtml() {
|
) : VisionOfHtmlControl() {
|
||||||
public var values: Meta? by properties.node()
|
public var values: Meta? by properties.node()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,3 +29,20 @@ public fun <R> TagConsumer<R>.bindForm(
|
|||||||
this.id = visionOfForm.formId
|
this.id = visionOfForm.formId
|
||||||
builder()
|
builder()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@SerialName("html.button")
|
||||||
|
public class VisionOfHtmlButton : VisionOfHtmlControl(), ClickControl {
|
||||||
|
public var label: String? by properties.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UnusedReceiverParameter")
|
||||||
|
public inline fun VisionOutput.button(
|
||||||
|
text: String,
|
||||||
|
block: VisionOfHtmlButton.() -> Unit = {},
|
||||||
|
): VisionOfHtmlButton = VisionOfHtmlButton().apply {
|
||||||
|
label = text
|
||||||
|
block()
|
||||||
|
}
|
@ -34,7 +34,13 @@ public interface ElementVisionRenderer : Named {
|
|||||||
* Display the [vision] inside a given [element] replacing its current content.
|
* Display the [vision] inside a given [element] replacing its current content.
|
||||||
* @param meta additional parameters for rendering container
|
* @param meta additional parameters for rendering container
|
||||||
*/
|
*/
|
||||||
public fun render(element: Element, name: Name, vision: Vision, meta: Meta = Meta.EMPTY)
|
public fun render(
|
||||||
|
element: Element,
|
||||||
|
client: VisionClient,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
meta: Meta = Meta.EMPTY,
|
||||||
|
)
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val TYPE: String = "elementVisionRenderer"
|
public const val TYPE: String = "elementVisionRenderer"
|
||||||
@ -49,7 +55,7 @@ public interface ElementVisionRenderer : Named {
|
|||||||
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, vision: T, meta: Meta) -> Unit,
|
private val renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit,
|
||||||
) : ElementVisionRenderer {
|
) : ElementVisionRenderer {
|
||||||
|
|
||||||
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||||
@ -60,15 +66,21 @@ public class SingleTypeVisionRenderer<T : Vision>(
|
|||||||
override fun rateVision(vision: Vision): Int =
|
override fun rateVision(vision: Vision): Int =
|
||||||
if (vision::class == kClass) acceptRating else ElementVisionRenderer.ZERO_RATING
|
if (vision::class == kClass) acceptRating else ElementVisionRenderer.ZERO_RATING
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(
|
||||||
|
element: Element,
|
||||||
|
client: VisionClient,
|
||||||
|
name: Name,
|
||||||
|
vision: Vision,
|
||||||
|
meta: Meta,
|
||||||
|
) {
|
||||||
element.clear()
|
element.clear()
|
||||||
element.append {
|
element.append {
|
||||||
renderFunction(name, kClass.cast(vision), meta)
|
renderFunction(name, client, 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, vision: T, meta: Meta) -> Unit,
|
noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, client: VisionClient, vision: T, meta: Meta) -> Unit,
|
||||||
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)
|
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)
|
||||||
|
@ -94,8 +94,9 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
|
|
||||||
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
||||||
vision.setAsRoot(visionManager)
|
vision.setAsRoot(visionManager)
|
||||||
val renderer = findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
val renderer: ElementVisionRenderer =
|
||||||
renderer.render(element, name, vision, outputMeta)
|
findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
||||||
|
renderer.render(element, this, 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) {
|
||||||
@ -134,7 +135,9 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug { "Got $event for output with name $visionName" }
|
logger.debug { "Got $event for output with name $visionName" }
|
||||||
vision.receiveEvent(event)
|
context.launch {
|
||||||
|
vision.receiveEvent(event)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error { "WebSocket message data is not a string" }
|
logger.error { "WebSocket message data is not a string" }
|
||||||
}
|
}
|
||||||
@ -261,7 +264,8 @@ public class JsVisionClient : AbstractPlugin(), VisionClient {
|
|||||||
numberVisionRenderer,
|
numberVisionRenderer,
|
||||||
textVisionRenderer,
|
textVisionRenderer,
|
||||||
rangeVisionRenderer,
|
rangeVisionRenderer,
|
||||||
formVisionRenderer
|
formVisionRenderer,
|
||||||
|
buttonVisionRenderer
|
||||||
).associateByName()
|
).associateByName()
|
||||||
} else super<AbstractPlugin>.content(target)
|
} else super<AbstractPlugin>.content(target)
|
||||||
|
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package space.kscience.visionforge
|
||||||
|
|
||||||
|
import kotlinx.browser.document
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.html.ButtonType
|
||||||
|
import kotlinx.html.js.button
|
||||||
|
import org.w3c.dom.HTMLFormElement
|
||||||
|
import org.w3c.dom.HTMLInputElement
|
||||||
|
import org.w3c.dom.get
|
||||||
|
import org.w3c.xhr.FormData
|
||||||
|
import space.kscience.dataforge.context.debug
|
||||||
|
import space.kscience.dataforge.context.logger
|
||||||
|
import space.kscience.dataforge.meta.*
|
||||||
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.visionforge.html.VisionOfHtmlButton
|
||||||
|
import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||||
|
|
||||||
|
|
||||||
|
internal fun FormData.toMeta(): Meta {
|
||||||
|
@Suppress("UNUSED_VARIABLE") val formData = this
|
||||||
|
//val res = js("Object.fromEntries(formData);")
|
||||||
|
val `object` = js("{}")
|
||||||
|
//language=JavaScript
|
||||||
|
js(
|
||||||
|
"""
|
||||||
|
formData.forEach(function(value, key){
|
||||||
|
// Reflect.has in favor of: object.hasOwnProperty(key)
|
||||||
|
if(!Reflect.has(object, key)){
|
||||||
|
object[key] = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!Array.isArray(object[key])){
|
||||||
|
object[key] = [object[key]];
|
||||||
|
}
|
||||||
|
object[key].push(value);
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return DynamicMeta(`object`)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public fun VisionClient.sendMetaEvent(targetName: Name, payload: MetaRepr): Unit {
|
||||||
|
context.launch {
|
||||||
|
sendEvent(targetName, VisionMetaEvent(payload.toMeta()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val formVisionRenderer: ElementVisionRenderer =
|
||||||
|
ElementVisionRenderer<VisionOfHtmlForm> { name, client, vision, _ ->
|
||||||
|
|
||||||
|
val form = document.getElementById(vision.formId) as? HTMLFormElement
|
||||||
|
?: error("An element with id = '${vision.formId} is not a form")
|
||||||
|
|
||||||
|
form.subscribeToVision(vision)
|
||||||
|
|
||||||
|
vision.manager?.logger?.debug { "Adding hooks to form with id = '$vision.formId'" }
|
||||||
|
|
||||||
|
vision.useProperty(VisionOfHtmlForm::values) { values ->
|
||||||
|
vision.manager?.logger?.debug { "Updating form '${vision.formId}' with values $values" }
|
||||||
|
val inputs = form.getElementsByTagName("input")
|
||||||
|
values?.valueSequence()?.forEach { (token, value) ->
|
||||||
|
(inputs[token.toString()] as? HTMLInputElement)?.value = value.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form.onsubmit = { event ->
|
||||||
|
event.preventDefault()
|
||||||
|
val formData = FormData(form).toMeta()
|
||||||
|
client.sendMetaEvent(name, formData)
|
||||||
|
console.info("Sent: ${formData.toMap()}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val buttonVisionRenderer: ElementVisionRenderer =
|
||||||
|
ElementVisionRenderer<VisionOfHtmlButton> { name, client, vision, _ ->
|
||||||
|
button(type = ButtonType.button).also { button ->
|
||||||
|
button.subscribeToVision(vision)
|
||||||
|
button.onclick = {
|
||||||
|
client.context.launch {
|
||||||
|
client.sendEvent(name, VisionClickEvent(name = name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vision.useProperty(VisionOfHtmlButton::label) {
|
||||||
|
button.innerHTML = it ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,18 @@
|
|||||||
package space.kscience.visionforge
|
package space.kscience.visionforge
|
||||||
|
|
||||||
import kotlinx.browser.document
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.dom.clear
|
||||||
import kotlinx.html.InputType
|
import kotlinx.html.InputType
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.js.input
|
import kotlinx.html.js.input
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
import org.w3c.dom.HTMLFormElement
|
|
||||||
import org.w3c.dom.HTMLInputElement
|
import org.w3c.dom.HTMLInputElement
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
import org.w3c.dom.get
|
import space.kscience.dataforge.meta.Value
|
||||||
import org.w3c.xhr.FormData
|
import space.kscience.dataforge.meta.asValue
|
||||||
import space.kscience.dataforge.context.debug
|
import space.kscience.dataforge.meta.double
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.meta.string
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.visionforge.html.*
|
import space.kscience.visionforge.html.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,13 +20,19 @@ import space.kscience.visionforge.html.*
|
|||||||
*
|
*
|
||||||
* @param vision The vision to subscribe to.
|
* @param vision The vision to subscribe to.
|
||||||
*/
|
*/
|
||||||
private fun HTMLElement.subscribeToVision(vision: VisionOfHtml) {
|
internal fun HTMLElement.subscribeToVision(vision: VisionOfHtml) {
|
||||||
vision.useProperty(VisionOfHtml::classes) {
|
vision.useProperty(VisionOfHtml::classes) {
|
||||||
classList.value = classes.joinToString(separator = " ")
|
classList.value = classes.joinToString(separator = " ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun VisionClient.sendInputEvent(name: Name, value: Value?) {
|
||||||
|
context.launch {
|
||||||
|
sendEvent(name, VisionValueChangeEvent(value, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes the HTML input element to a given vision.
|
* Subscribes the HTML input element to a given vision.
|
||||||
*
|
*
|
||||||
@ -40,46 +46,48 @@ 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) {
|
||||||
div.textContent = it
|
div.clear()
|
||||||
|
if (it != null) div.innerHTML = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val inputVisionRenderer: ElementVisionRenderer =
|
internal val inputVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlInput>(
|
||||||
ElementVisionRenderer<VisionOfHtmlInput>(acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1) { _, vision, _ ->
|
acceptRating = ElementVisionRenderer.DEFAULT_RATING - 1
|
||||||
input {
|
) { name, client, vision, _ ->
|
||||||
type = InputType.text
|
input {
|
||||||
}.also { htmlInputElement ->
|
type = InputType.text
|
||||||
val onEvent: (Event) -> Unit = {
|
}.also { htmlInputElement ->
|
||||||
vision.value = htmlInputElement.value.asValue()
|
val onEvent: (Event) -> Unit = {
|
||||||
}
|
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
when (vision.feedbackMode) {
|
when (vision.feedbackMode) {
|
||||||
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
InputFeedbackMode.ONCHANGE -> htmlInputElement.onchange = onEvent
|
||||||
|
|
||||||
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
InputFeedbackMode.ONINPUT -> htmlInputElement.oninput = onEvent
|
||||||
InputFeedbackMode.NONE -> {}
|
InputFeedbackMode.NONE -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlInputElement.subscribeToInput(vision)
|
htmlInputElement.subscribeToInput(vision)
|
||||||
vision.useProperty(VisionOfHtmlInput::value) {
|
vision.useProperty(VisionOfHtmlInput::value) {
|
||||||
htmlInputElement.value = it?.string ?: ""
|
htmlInputElement.value = it?.string ?: ""
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal val checkboxVisionRenderer: ElementVisionRenderer =
|
internal val checkboxVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfCheckbox> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfCheckbox> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.checkBox
|
type = InputType.checkBox
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
vision.checked = htmlInputElement.checked
|
client.sendInputEvent(name, htmlInputElement.checked.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -98,12 +106,12 @@ internal val checkboxVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val textVisionRenderer: ElementVisionRenderer =
|
internal val textVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfTextField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfTextField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.text
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
vision.text = htmlInputElement.value
|
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,13 +130,15 @@ internal val textVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val numberVisionRenderer: ElementVisionRenderer =
|
internal val numberVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfNumberField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfNumberField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.number
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
|
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||||
|
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (vision.feedbackMode) {
|
when (vision.feedbackMode) {
|
||||||
@ -145,16 +155,18 @@ internal val numberVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val rangeVisionRenderer: ElementVisionRenderer =
|
internal val rangeVisionRenderer: ElementVisionRenderer =
|
||||||
ElementVisionRenderer<VisionOfRangeField> { _, vision, _ ->
|
ElementVisionRenderer<VisionOfRangeField> { name, client, vision, _ ->
|
||||||
input {
|
input {
|
||||||
type = InputType.text
|
type = InputType.range
|
||||||
min = vision.min.toString()
|
min = vision.min.toString()
|
||||||
max = vision.max.toString()
|
max = vision.max.toString()
|
||||||
step = vision.step.toString()
|
step = vision.step.toString()
|
||||||
}.also { htmlInputElement ->
|
}.also { htmlInputElement ->
|
||||||
|
|
||||||
val onEvent: (Event) -> Unit = {
|
val onEvent: (Event) -> Unit = {
|
||||||
htmlInputElement.value.toDoubleOrNull()?.let { vision.number = it }
|
htmlInputElement.value.toDoubleOrNull()?.let {
|
||||||
|
client.sendInputEvent(name, htmlInputElement.value.asValue())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (vision.feedbackMode) {
|
when (vision.feedbackMode) {
|
||||||
@ -169,53 +181,3 @@ internal val rangeVisionRenderer: ElementVisionRenderer =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun FormData.toMeta(): Meta {
|
|
||||||
@Suppress("UNUSED_VARIABLE") val formData = this
|
|
||||||
//val res = js("Object.fromEntries(formData);")
|
|
||||||
val `object` = js("{}")
|
|
||||||
//language=JavaScript
|
|
||||||
js(
|
|
||||||
"""
|
|
||||||
formData.forEach(function(value, key){
|
|
||||||
// Reflect.has in favor of: object.hasOwnProperty(key)
|
|
||||||
if(!Reflect.has(object, key)){
|
|
||||||
object[key] = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!Array.isArray(object[key])){
|
|
||||||
object[key] = [object[key]];
|
|
||||||
}
|
|
||||||
object[key].push(value);
|
|
||||||
});
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
return DynamicMeta(`object`)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val formVisionRenderer: ElementVisionRenderer =
|
|
||||||
ElementVisionRenderer<VisionOfHtmlForm> { _, vision, _ ->
|
|
||||||
|
|
||||||
val form = document.getElementById(vision.formId) as? HTMLFormElement
|
|
||||||
?: error("An element with id = '${vision.formId} is not a form")
|
|
||||||
|
|
||||||
form.subscribeToVision(vision)
|
|
||||||
|
|
||||||
vision.manager?.logger?.debug { "Adding hooks to form with id = '$vision.formId'" }
|
|
||||||
|
|
||||||
vision.useProperty(VisionOfHtmlForm::values) { values ->
|
|
||||||
vision.manager?.logger?.debug { "Updating form '${vision.formId}' with values $values" }
|
|
||||||
val inputs = form.getElementsByTagName("input")
|
|
||||||
values?.valueSequence()?.forEach { (token, value) ->
|
|
||||||
(inputs[token.toString()] as? HTMLInputElement)?.value = value.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
form.onsubmit = { event ->
|
|
||||||
event.preventDefault()
|
|
||||||
val formData = FormData(form).toMeta()
|
|
||||||
vision.values = formData
|
|
||||||
console.info("Sent: ${formData.toMap()}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
|
|||||||
else -> ElementVisionRenderer.ZERO_RATING
|
else -> ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, client: VisionClient, 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) {
|
||||||
|
@ -10,10 +10,7 @@ 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.ElementVisionRenderer
|
import space.kscience.visionforge.*
|
||||||
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)
|
||||||
@ -27,7 +24,7 @@ public actual class PlotlyPlugin : VisionPlugin(), ElementVisionRenderer {
|
|||||||
else -> ElementVisionRenderer.ZERO_RATING
|
else -> ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, client: VisionClient, 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)
|
||||||
|
@ -14,6 +14,7 @@ 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
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ public class TableVisionJsPlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
else -> ElementVisionRenderer.ZERO_RATING
|
else -> ElementVisionRenderer.ZERO_RATING
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, client: VisionClient, 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")
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ public class ThreePlugin : AbstractPlugin(), ElementVisionRenderer {
|
|||||||
render(vision)
|
render(vision)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
override fun render(element: Element, client: VisionClient, name: Name, vision: Vision, meta: Meta) {
|
||||||
renderSolid(
|
renderSolid(
|
||||||
element,
|
element,
|
||||||
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
vision as? Solid ?: error("Solid expected but ${vision::class} found"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user