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