forked from kscience/visionforge
Refactor server API
This commit is contained in:
parent
c8141c6338
commit
fd8f693151
@ -34,7 +34,7 @@ public interface ElementVisionRenderer : Named {
|
||||
* Display the [vision] inside a given [element] replacing its current content.
|
||||
* @param meta additional parameters for rendering container
|
||||
*/
|
||||
public fun render(element: Element, vision: Vision, meta: Meta = Meta.EMPTY)
|
||||
public fun render(element: Element, name: Name, vision: Vision, meta: Meta = Meta.EMPTY)
|
||||
|
||||
public companion object {
|
||||
public const val TYPE: String = "elementVisionRenderer"
|
||||
@ -49,7 +49,7 @@ public interface ElementVisionRenderer : Named {
|
||||
public class SingleTypeVisionRenderer<T : Vision>(
|
||||
public val kClass: KClass<T>,
|
||||
private val acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
||||
private val renderFunction: TagConsumer<HTMLElement>.(vision: T, meta: Meta) -> Unit,
|
||||
private val renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
|
||||
) : ElementVisionRenderer {
|
||||
|
||||
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||
@ -60,15 +60,15 @@ public class SingleTypeVisionRenderer<T : Vision>(
|
||||
override fun rateVision(vision: Vision): Int =
|
||||
if (vision::class == kClass) acceptRating else ElementVisionRenderer.ZERO_RATING
|
||||
|
||||
override fun render(element: Element, vision: Vision, meta: Meta) {
|
||||
override fun render(element: Element, name: Name, vision: Vision, meta: Meta) {
|
||||
element.clear()
|
||||
element.append {
|
||||
renderFunction(kClass.cast(vision), meta)
|
||||
renderFunction(name, kClass.cast(vision), meta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T : Vision> ElementVisionRenderer(
|
||||
acceptRating: Int = ElementVisionRenderer.DEFAULT_RATING,
|
||||
noinline renderFunction: TagConsumer<HTMLElement>.(vision: T, meta: Meta) -> Unit,
|
||||
noinline renderFunction: TagConsumer<HTMLElement>.(name: Name, vision: T, meta: Meta) -> Unit,
|
||||
): ElementVisionRenderer = SingleTypeVisionRenderer(T::class, acceptRating, renderFunction)
|
||||
|
@ -13,6 +13,7 @@ import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.int
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.parseAsName
|
||||
import space.kscience.visionforge.html.VisionTagConsumer
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_CONNECT_ATTRIBUTE
|
||||
import space.kscience.visionforge.html.VisionTagConsumer.Companion.OUTPUT_ENDPOINT_ATTRIBUTE
|
||||
@ -67,17 +68,29 @@ public class VisionClient : AbstractPlugin() {
|
||||
changeCollector.propertyChanged(visionName, propertyName, item)
|
||||
}
|
||||
|
||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Boolean) {
|
||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
||||
}
|
||||
|
||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: String) {
|
||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
||||
}
|
||||
|
||||
public fun visionPropertyChanged(visionName: Name, propertyName: Name, item: Number) {
|
||||
visionPropertyChanged(visionName, propertyName, Meta(item))
|
||||
}
|
||||
|
||||
public fun visionChanged(name: Name?, child: Vision?) {
|
||||
changeCollector.setChild(name, child)
|
||||
}
|
||||
|
||||
private fun renderVision(element: Element, vision: Vision, outputMeta: Meta) {
|
||||
private fun renderVision(element: Element, name: Name, vision: Vision, outputMeta: Meta) {
|
||||
vision.setAsRoot(visionManager)
|
||||
val renderer = findRendererFor(vision) ?: error("Could not find renderer for ${vision::class}")
|
||||
renderer.render(element, vision, outputMeta)
|
||||
renderer.render(element, name, vision, outputMeta)
|
||||
}
|
||||
|
||||
private fun updateVision(name: String, element: Element, vision: Vision?, outputMeta: Meta) {
|
||||
private fun updateVision(element: Element, name: Name, vision: Vision?, outputMeta: Meta) {
|
||||
element.attributes[OUTPUT_CONNECT_ATTRIBUTE]?.let { attr ->
|
||||
val wsUrl = if (attr.value.isBlank() || attr.value == VisionTagConsumer.AUTO_DATA_ATTRIBUTE) {
|
||||
val endpoint = resolveEndpoint(element)
|
||||
@ -89,7 +102,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
URL(attr.value)
|
||||
}.apply {
|
||||
protocol = "ws"
|
||||
searchParams.append("name", name)
|
||||
searchParams.append("name", name.toString())
|
||||
}
|
||||
|
||||
logger.info { "Updating vision data from $wsUrl" }
|
||||
@ -106,7 +119,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
|
||||
// If change contains root vision replacement, do it
|
||||
change.vision?.let { vision ->
|
||||
renderVision(element, vision, outputMeta)
|
||||
renderVision(element, name, vision, outputMeta)
|
||||
}
|
||||
|
||||
logger.debug { "Got update $change for output with name $name" }
|
||||
@ -152,7 +165,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
*/
|
||||
public fun renderVisionIn(element: Element) {
|
||||
if (!element.classList.contains(VisionTagConsumer.OUTPUT_CLASS)) error("The element $element is not an output element")
|
||||
val name = resolveName(element) ?: error("The element is not a vision output")
|
||||
val name = resolveName(element)?.parseAsName() ?: error("The element is not a vision output")
|
||||
|
||||
if (element.attributes[OUTPUT_RENDERED]?.value == "true") {
|
||||
logger.info { "VF output in element $element is already rendered" }
|
||||
@ -179,7 +192,7 @@ public class VisionClient : AbstractPlugin() {
|
||||
} else {
|
||||
URL(attr.value)
|
||||
}.apply {
|
||||
searchParams.append("name", name)
|
||||
searchParams.append("name", name.toString())
|
||||
}
|
||||
|
||||
logger.info { "Fetching vision data from $fetchUrl" }
|
||||
@ -187,8 +200,8 @@ public class VisionClient : AbstractPlugin() {
|
||||
if (response.ok) {
|
||||
response.text().then { text ->
|
||||
val vision = visionManager.decodeFromString(text)
|
||||
renderVision(element, vision, outputMeta)
|
||||
updateVision(name, element, vision, outputMeta)
|
||||
renderVision(element, name, vision, outputMeta)
|
||||
updateVision(element, name, vision, outputMeta)
|
||||
}
|
||||
} else {
|
||||
logger.error { "Failed to fetch initial vision state from $fetchUrl" }
|
||||
@ -203,13 +216,13 @@ public class VisionClient : AbstractPlugin() {
|
||||
visionManager.decodeFromString(it)
|
||||
}
|
||||
logger.info { "Found embedded vision for output with name $name" }
|
||||
renderVision(element, embeddedVision, outputMeta)
|
||||
updateVision(name, element, embeddedVision, outputMeta)
|
||||
renderVision(element, name, embeddedVision, outputMeta)
|
||||
updateVision(element, name, embeddedVision, outputMeta)
|
||||
}
|
||||
|
||||
//Try to load vision via websocket
|
||||
element.attributes[OUTPUT_CONNECT_ATTRIBUTE] != null -> {
|
||||
updateVision(name, element, null, outputMeta)
|
||||
updateVision(element, name, null, outputMeta)
|
||||
}
|
||||
|
||||
else -> error("No embedded vision data / fetch url for $name")
|
||||
@ -217,11 +230,13 @@ public class VisionClient : AbstractPlugin() {
|
||||
element.setAttribute(OUTPUT_RENDERED, "true")
|
||||
}
|
||||
|
||||
override fun content(target: String): Map<Name, Any> = if (target == ElementVisionRenderer.TYPE) mapOf(
|
||||
numberVisionRenderer.name to numberVisionRenderer,
|
||||
textVisionRenderer.name to textVisionRenderer,
|
||||
formVisionRenderer.name to formVisionRenderer
|
||||
) else super.content(target)
|
||||
override fun content(target: String): Map<Name, Any> = if (target == ElementVisionRenderer.TYPE) {
|
||||
listOf(
|
||||
numberVisionRenderer(this),
|
||||
textVisionRenderer(this),
|
||||
formVisionRenderer(this)
|
||||
).toMap()
|
||||
} else super.content(target)
|
||||
|
||||
public companion object : PluginFactory<VisionClient> {
|
||||
override fun build(context: Context, meta: Meta): VisionClient = VisionClient()
|
||||
|
@ -16,42 +16,46 @@ import space.kscience.visionforge.html.VisionOfHtmlForm
|
||||
import space.kscience.visionforge.html.VisionOfNumberField
|
||||
import space.kscience.visionforge.html.VisionOfTextField
|
||||
|
||||
public val textVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfTextField> { vision, _ ->
|
||||
val name = vision.name ?: "input[${vision.hashCode().toUInt()}]"
|
||||
internal fun textVisionRenderer(
|
||||
client: VisionClient,
|
||||
): ElementVisionRenderer = ElementVisionRenderer<VisionOfTextField> { name, vision, _ ->
|
||||
val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]"
|
||||
vision.label?.let {
|
||||
label {
|
||||
htmlFor = name
|
||||
htmlFor = fieldName
|
||||
+it
|
||||
}
|
||||
}
|
||||
input {
|
||||
type = InputType.text
|
||||
this.name = name
|
||||
this.name = fieldName
|
||||
vision.useProperty(VisionOfTextField::text) {
|
||||
value = it ?: ""
|
||||
}
|
||||
onChangeFunction = {
|
||||
vision.text = value
|
||||
// client.visionPropertyChanged(name, VisionOfTextField::text.name.pa, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public val numberVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfNumberField> { vision, _ ->
|
||||
val name = vision.name ?: "input[${vision.hashCode().toUInt()}]"
|
||||
internal fun numberVisionRenderer(
|
||||
client: VisionClient,
|
||||
): ElementVisionRenderer = ElementVisionRenderer<VisionOfNumberField> { name, vision, _ ->
|
||||
val fieldName = vision.name ?: "input[${vision.hashCode().toUInt()}]"
|
||||
vision.label?.let {
|
||||
label {
|
||||
htmlFor = name
|
||||
htmlFor = fieldName
|
||||
+it
|
||||
}
|
||||
}
|
||||
input {
|
||||
type = InputType.text
|
||||
this.name = name
|
||||
this.name = fieldName
|
||||
vision.useProperty(VisionOfNumberField::value) {
|
||||
value = it?.toDouble() ?: 0.0
|
||||
}
|
||||
onChangeFunction = {
|
||||
vision.value = value.toDoubleOrNull()
|
||||
// vision.value = value.toDoubleOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,7 +65,8 @@ internal fun FormData.toMeta(): Meta {
|
||||
//val res = js("Object.fromEntries(formData);")
|
||||
val `object` = js("{}")
|
||||
//language=JavaScript
|
||||
js("""
|
||||
js(
|
||||
"""
|
||||
formData.forEach(function(value, key){
|
||||
// Reflect.has in favor of: object.hasOwnProperty(key)
|
||||
if(!Reflect.has(object, key)){
|
||||
@ -73,11 +78,14 @@ internal fun FormData.toMeta(): Meta {
|
||||
}
|
||||
object[key].push(value);
|
||||
});
|
||||
""")
|
||||
"""
|
||||
)
|
||||
return DynamicMeta(`object`)
|
||||
}
|
||||
|
||||
public val formVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlForm> { vision, _ ->
|
||||
internal fun formVisionRenderer(
|
||||
client: VisionClient,
|
||||
): ElementVisionRenderer = ElementVisionRenderer<VisionOfHtmlForm> { name, vision, _ ->
|
||||
|
||||
val form = document.getElementById(vision.formId) as? HTMLFormElement
|
||||
?: error("An element with id = '${vision.formId} is not a form")
|
||||
@ -95,7 +103,7 @@ public val formVisionRenderer: ElementVisionRenderer = ElementVisionRenderer<Vis
|
||||
event.preventDefault()
|
||||
val formData = FormData(form).toMeta()
|
||||
//console.log(formData.toString())
|
||||
vision.values = formData
|
||||
//vision.values = formData
|
||||
false
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.PluginFactory
|
||||
import space.kscience.dataforge.context.PluginTag
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.visionforge.*
|
||||
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.COMMONMARK_FORMAT
|
||||
import space.kscience.visionforge.markup.VisionOfMarkup.Companion.GFM_FORMAT
|
||||
@ -26,7 +27,7 @@ public actual class MarkupPlugin : VisionPlugin(), ElementVisionRenderer {
|
||||
else -> ElementVisionRenderer.ZERO_RATING
|
||||
}
|
||||
|
||||
override fun render(element: Element, 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" }
|
||||
val div = document.createElement("div")
|
||||
val flavour = when (vision.format) {
|
||||
|
Loading…
Reference in New Issue
Block a user