diff --git a/build.gradle.kts b/build.gradle.kts index c1218820..b7ee4ffd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,10 @@ -import scientifik.useSerialization +import scientifik.fx +import scientifik.serialization -val dataforgeVersion by extra("0.1.5-dev-6") +val dataforgeVersion by extra("0.1.7") plugins { - val kotlinVersion = "1.3.61" - val toolsVersion = "0.3.2" - - kotlin("jvm") version kotlinVersion apply false - id("kotlin-dce-js") version kotlinVersion apply false + val toolsVersion = "0.4.2" id("scientifik.mpp") version toolsVersion apply false id("scientifik.jvm") version toolsVersion apply false id("scientifik.js") version toolsVersion apply false @@ -21,21 +18,23 @@ allprojects { maven("https://dl.bintray.com/pdvrieze/maven") maven("http://maven.jzy3d.org/releases") maven("https://kotlin.bintray.com/js-externals") + maven("https://kotlin.bintray.com/kotlin-js-wrappers/") // maven("https://dl.bintray.com/gbaldeck/kotlin") // maven("https://dl.bintray.com/rjaros/kotlin") } group = "hep.dataforge" - version = "0.1.1-dev" -} - -subprojects{ - this.useSerialization() + version = "0.1.3-dev" } val githubProject by extra("dataforge-vis") val bintrayRepo by extra("dataforge") +val fxVersion by extra("14") subprojects { apply(plugin = "scientifik.publish") + serialization() + afterEvaluate { + fx(scientifik.FXModule.CONTROLS, version = fxVersion) + } } \ No newline at end of file diff --git a/dataforge-vis-common/build.gradle.kts b/dataforge-vis-common/build.gradle.kts index 6459c337..465b03c2 100644 --- a/dataforge-vis-common/build.gradle.kts +++ b/dataforge-vis-common/build.gradle.kts @@ -1,50 +1,47 @@ -import org.openjfx.gradle.JavaFXOptions -import scientifik.useSerialization - plugins { id("scientifik.mpp") - id("org.openjfx.javafxplugin") } val dataforgeVersion: String by rootProject.extra //val kvisionVersion: String by rootProject.extra("2.0.0-M1") -useSerialization() - kotlin { - jvm{ - withJava() - } - sourceSets { - commonMain{ + commonMain { dependencies { api("hep.dataforge:dataforge-output:$dataforgeVersion") } } - jvmMain{ + jvmMain { dependencies { - api("no.tornado:tornadofx:1.7.19") + api("no.tornado:tornadofx:1.7.20") //api("no.tornado:tornadofx-controlsfx:0.1.1") - api("de.jensd:fontawesomefx-fontawesome:4.7.0-11"){ + api("de.jensd:fontawesomefx-fontawesome:4.7.0-11") { exclude(group = "org.openjfx") } - api("de.jensd:fontawesomefx-commons:11.0"){ + api("de.jensd:fontawesomefx-commons:11.0") { exclude(group = "org.openjfx") } } } - jsMain{ + jsMain { dependencies { api("hep.dataforge:dataforge-output-html:$dataforgeVersion") - //api(npm("bootstrap","4.4.1")) - implementation(npm("jsoneditor")) - implementation(npm("file-saver")) + + //React, React DOM + Wrappers (chapter 3) + api("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70") + api("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70") + api(npm("react", "16.13.0")) + api(npm("react-dom", "16.13.0")) + + //Kotlin Styled (chapter 3) + api("org.jetbrains:kotlin-styled:1.0.0-pre.94-kotlin-1.3.70") + api(npm("styled-components")) + api(npm("inline-style-prefixer")) + + api(npm("source-map-resolve","0.6.0")) + api(npm("file-saver","2.0.2")) } } } -} - -configure { - modules("javafx.controls") -} +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt similarity index 63% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt index a0af25b7..d130b5c7 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualGroup.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis import hep.dataforge.meta.MetaItem import hep.dataforge.names.Name @@ -20,6 +20,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup */ abstract override val children: Map + abstract override var styleSheet: StyleSheet? + protected set + + /** + * Update or create stylesheet + */ + fun styleSheet(block: StyleSheet.() -> Unit) { + val res = styleSheet ?: StyleSheet(this).also { styleSheet = it } + res.block() + } + override fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?) { super.propertyChanged(name, before, after) forEach { @@ -37,7 +48,12 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup * Add listener for children change */ override fun onChildrenChange(owner: Any?, action: (Name, VisualObject?) -> Unit) { - structureChangeListeners.add(StructureChangeListeners(owner, action)) + structureChangeListeners.add( + StructureChangeListeners( + owner, + action + ) + ) } /** @@ -68,12 +84,41 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup * Add a static child. Statics could not be found by name, removed or replaced */ protected open fun addStatic(child: VisualObject) = - setChild(NameToken("@static(${child.hashCode()})"), child) + set(NameToken("@static(${child.hashCode()})").asName(), child) + + protected abstract fun createGroup(): AbstractVisualGroup + + /** + * Set this node as parent for given node + */ + protected fun attach(child: VisualObject) { + if (child.parent == null) { + child.parent = this + } else if (child.parent !== this) { + error("Can't reassign existing parent for $child") + } + } /** * Recursively create a child group */ - protected abstract fun createGroup(name: Name): MutableVisualGroup + private fun createGroups(name: Name): AbstractVisualGroup { + return when { + name.isEmpty() -> error("Should be unreachable") + name.length == 1 -> { + val token = name.first()!! + when (val current = children[token]) { + null -> createGroup().also { child -> + attach(child) + setChild(token, child) + } + is AbstractVisualGroup -> current + else -> error("Can't create group with name $name because it exists and not a group") + } + } + else -> createGroups(name.first()!!.asName()).createGroups(name.cutFirst()) + } + } /** * Add named or unnamed child to the group. If key is null the child is considered unnamed. Both key and value are not @@ -91,16 +136,17 @@ abstract class AbstractVisualGroup : AbstractVisualObject(), MutableVisualGroup if (child == null) { removeChild(token) } else { + attach(child) setChild(token, child) } } else -> { //TODO add safety check - val parent = (get(name.cutLast()) as? MutableVisualGroup) ?: createGroup(name.cutLast()) + val parent = (get(name.cutLast()) as? MutableVisualGroup) ?: createGroups(name.cutLast()) parent[name.last()!!.asName()] = child } } - structureChangeListeners.forEach { it.callback(name, child) } + childrenChanged(name, child) } } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt similarity index 90% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt index 5487a304..e355fa94 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/AbstractVisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/AbstractVisualObject.kt @@ -1,9 +1,10 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName -import hep.dataforge.vis.common.VisualObject.Companion.STYLE_KEY +import hep.dataforge.values.Value +import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY import kotlinx.serialization.Transient internal data class PropertyListener( @@ -14,7 +15,7 @@ internal data class PropertyListener( abstract class AbstractVisualObject : VisualObject { @Transient - override var parent: VisualObject? = null + override var parent: VisualGroup? = null protected abstract var properties: Config? @@ -22,7 +23,7 @@ abstract class AbstractVisualObject : VisualObject { get() = properties?.get(STYLE_KEY).stringList set(value) { //val allStyles = (field + value).distinct() - setProperty(STYLE_KEY, value) + setProperty(STYLE_KEY, Value.of(value)) updateStyles(value) } @@ -66,7 +67,7 @@ abstract class AbstractVisualObject : VisualObject { private var styleCache: Meta? = null /** - * Collect all styles for this object in a laminate + * Collect all styles for this object in a single cached meta */ protected val mergedStyles: Meta get() = styleCache ?: findAllStyles().merge().also { diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Colors.kt similarity index 97% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Colors.kt index 3335c086..b91dbcff 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Colors.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Colors.kt @@ -1,6 +1,9 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis -import hep.dataforge.meta.* +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.get +import hep.dataforge.meta.number import hep.dataforge.values.ValueType import hep.dataforge.values.int import kotlin.math.max @@ -234,7 +237,7 @@ object Colors { /** * Convert three bytes representing color to Meta */ - fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = buildMeta { + fun rgbToMeta(r: UByte, g: UByte, b: UByte): Meta = Meta { RED_KEY put r.toInt() GREEN_KEY put g.toInt() BLUE_KEY put b.toInt() diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt new file mode 100644 index 00000000..468a5264 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/SimpleVisualGroup.kt @@ -0,0 +1,31 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.Config +import hep.dataforge.names.NameToken +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + + +@Serializable +@SerialName("group") +class SimpleVisualGroup : AbstractVisualGroup() { + + override var styleSheet: StyleSheet? = null + + //FIXME to be lifted to AbstractVisualGroup after https://github.com/Kotlin/kotlinx.serialization/issues/378 is fixed + override var properties: Config? = null + + @SerialName("children") + private val _children = HashMap() + override val children: Map get() = _children + + override fun removeChild(token: NameToken) { + _children.remove(token)?.apply { parent = null } + } + + override fun setChild(token: NameToken, child: VisualObject) { + _children[token] = child + } + + override fun createGroup(): SimpleVisualGroup = SimpleVisualGroup() +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/StyleSheet.kt similarity index 64% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/StyleSheet.kt index 7b70a0dc..b244c410 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/StyleSheet.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/StyleSheet.kt @@ -1,18 +1,19 @@ @file:UseSerializers(MetaSerializer::class) -package hep.dataforge.vis.common +package hep.dataforge.vis -import hep.dataforge.io.serialization.MetaSerializer import hep.dataforge.meta.* import hep.dataforge.names.Name import hep.dataforge.names.asName import kotlinx.serialization.* +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer /** * A container for styles */ @Serializable -class StyleSheet() { +class StyleSheet private constructor(private val styleMap: MutableMap = LinkedHashMap()) { @Transient internal var owner: VisualObject? = null @@ -20,12 +21,10 @@ class StyleSheet() { this.owner = owner } - private val styleMap = HashMap() - val items: Map get() = styleMap operator fun get(key: String): Meta? { - return styleMap[key] ?: (owner?.parent as? VisualGroup)?.styleSheet?.get(key) + return styleMap[key] ?: owner?.parent?.styleSheet?.get(key) } /** @@ -46,20 +45,23 @@ class StyleSheet() { } operator fun set(key: String, builder: MetaBuilder.() -> Unit) { - val newStyle = get(key)?.let { buildMeta(it, builder) } ?: buildMeta(builder) + val newStyle = get(key)?.edit(builder) ?: Meta(builder) set(key, newStyle.seal()) } - companion object: KSerializer{ - override val descriptor: SerialDescriptor - get() = TODO("Not yet implemented") + @Serializer(StyleSheet::class) + companion object : KSerializer { + private val mapSerializer = MapSerializer(String.serializer(), MetaSerializer) + override val descriptor: SerialDescriptor get() = mapSerializer.descriptor + override fun deserialize(decoder: Decoder): StyleSheet { - TODO("Not yet implemented") + val map = mapSerializer.deserialize(decoder) + return StyleSheet(map as? MutableMap ?: LinkedHashMap(map)) } - override fun serialize(encoder: Encoder, obj: StyleSheet) { - TODO("Not yet implemented") + override fun serialize(encoder: Encoder, value: StyleSheet) { + mapSerializer.serialize(encoder, value.items) } } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt similarity index 95% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt index a469ac63..8aeb32a4 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/Visual.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/Visual.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis import hep.dataforge.context.* import hep.dataforge.meta.Meta @@ -31,7 +31,8 @@ class Visual(meta: Meta) : AbstractPlugin(meta) { override val tag: PluginTag = PluginTag(name = "visual", group = PluginTag.DATAFORGE_GROUP) override val type: KClass = Visual::class - override fun invoke(meta: Meta, context: Context): Visual = Visual(meta) + override fun invoke(meta: Meta, context: Context): Visual = + Visual(meta) const val VISUAL_FACTORY_TYPE = "visual.factory" } diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualGroup.kt similarity index 95% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualGroup.kt index c3f497bd..d077a269 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualGroup.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualGroup.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis import hep.dataforge.names.* import hep.dataforge.provider.Provider @@ -6,7 +6,8 @@ import hep.dataforge.provider.Provider /** * Represents a group of [VisualObject] instances */ -interface VisualGroup : Provider, Iterable, VisualObject { +interface VisualGroup : Provider, Iterable, + VisualObject { /** * A map of top level named children */ diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt similarity index 83% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt index 6aad894a..b757fcb3 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObject.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObject.kt @@ -1,11 +1,15 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis -import hep.dataforge.meta.* +import hep.dataforge.meta.Configurable +import hep.dataforge.meta.Laminate +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem import hep.dataforge.names.Name import hep.dataforge.names.asName import hep.dataforge.names.toName import hep.dataforge.provider.Type -import hep.dataforge.vis.common.VisualObject.Companion.TYPE +import hep.dataforge.vis.VisualObject.Companion.TYPE +import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.Transient //private fun Laminate.withTop(meta: Meta): Laminate = Laminate(listOf(meta) + layers) @@ -21,24 +25,19 @@ interface VisualObject : Configurable { * The parent object of this one. If null, this one is a root. */ @Transient - var parent: VisualObject? + var parent: VisualGroup? /** * All properties including styles and prototypes if present, but without inheritance */ fun allProperties(): Laminate - /** - * Set property for this object - */ - fun setProperty(name: Name, value: Any?) { - config[name] = value - } - /** * Get property including or excluding parent properties */ - fun getProperty(name: Name, inherit: Boolean = true): MetaItem<*>? + fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? + + override fun getProperty(name: Name): MetaItem<*>? = getProperty(name, true) /** * Trigger property invalidation event. If [name] is empty, notify that the whole object is changed @@ -66,6 +65,10 @@ interface VisualObject : Configurable { const val TYPE = "visual" val STYLE_KEY = "@style".asName() + private val VISUAL_OBJECT_SERIALIZER = PolymorphicSerializer(VisualObject::class) + + fun serializer() = VISUAL_OBJECT_SERIALIZER + //const val META_KEY = "@meta" //const val TAGS_KEY = "@tags" @@ -77,11 +80,6 @@ interface VisualObject : Configurable { */ fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit) -/** - * Set [VisualObject] property using key as a String - */ -fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value) - /** * Add style name to the list of styles to be resolved later. The style with given name does not necessary exist at the moment. */ diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt similarity index 98% rename from dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt rename to dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt index ec16f977..d0798522 100644 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/VisualObjectDelegate.kt +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/VisualObjectDelegate.kt @@ -1,4 +1,4 @@ -package hep.dataforge.vis.common +package hep.dataforge.vis import hep.dataforge.meta.* import hep.dataforge.names.Name @@ -105,8 +105,8 @@ fun VisualObject.int(default: Int, name: Name? = null, inherited: Boolean = fals inline fun > VisualObject.enum(default: E, name: Name? = null, inherited: Boolean = false) = - VisualObjectDelegateWrapper(this, name, default, inherited) { - item -> item.string?.let { enumValueOf(it) } + VisualObjectDelegateWrapper(this, name, default, inherited) { item -> + item.string?.let { enumValueOf(it) } } //merge properties diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/valueWidget.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/valueWidget.kt deleted file mode 100644 index 8c45686f..00000000 --- a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/common/valueWidget.kt +++ /dev/null @@ -1,22 +0,0 @@ -package hep.dataforge.vis.common - -import hep.dataforge.descriptors.ValueDescriptor -import hep.dataforge.meta.* - -/** - * Extension property to access the "widget" key of [ValueDescriptor] - */ -var ValueDescriptor.widget: Meta - get() = this.config["widget"].node?: EmptyMeta - set(value) { - this.config["widget"] = value - } - -/** - * Extension property to access the "widget.type" key of [ValueDescriptor] - */ -var ValueDescriptor.widgetType: String? - get() = this["widget.type"].string - set(value) { - this.config["widget.type"] = value - } \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/misc.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/misc.kt new file mode 100644 index 00000000..756fd42e --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/misc.kt @@ -0,0 +1,19 @@ +package hep.dataforge.vis + +import hep.dataforge.names.Name +import hep.dataforge.names.isEmpty + +/** + * Return nearest selectable parent [Name] + */ +tailrec fun Name.selectable(): Name? = when { + isEmpty() -> { + null + } + last()?.body?.startsWith("@") != true -> { + this + } + else -> { + cutLast().selectable() + } +} \ No newline at end of file diff --git a/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt new file mode 100644 index 00000000..80e44a35 --- /dev/null +++ b/dataforge-vis-common/src/commonMain/kotlin/hep/dataforge/vis/valueWidget.kt @@ -0,0 +1,23 @@ +package hep.dataforge.vis + +import hep.dataforge.meta.* +import hep.dataforge.meta.descriptors.ValueDescriptor +import hep.dataforge.values.asValue + +/** + * Extension property to access the "widget" key of [ValueDescriptor] + */ +var ValueDescriptor.widget: Meta + get() = getProperty("widget").node ?: Meta.EMPTY + set(value) { + setProperty("widget", value) + } + +/** + * Extension property to access the "widget.type" key of [ValueDescriptor] + */ +var ValueDescriptor.widgetType: String? + get() = getProperty("widget.type").string + set(value) { + setProperty("widget.type", value?.asValue()) + } \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/bootstrap.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/bootstrap.kt new file mode 100644 index 00000000..eb99704a --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/bootstrap.kt @@ -0,0 +1,121 @@ +package hep.dataforge.js + +import kotlinx.html.* +import kotlinx.html.js.div +import org.w3c.dom.HTMLElement +import react.RBuilder +import react.dom.* + +inline fun TagConsumer.card(title: String, crossinline block: TagConsumer.() -> Unit) { + div("card w-100") { + div("card-body") { + h3(classes = "card-title") { +title } + block() + } + } +} + +inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit) { + div("card w-100") { + div("card-body") { + h3(classes = "card-title") { +title } + block() + } + } +} + + +fun TagConsumer.accordion(id: String, elements: List Unit>>) { + div("container-fluid") { + div("accordion") { + this.id = id + elements.forEachIndexed { index, (title, builder) -> + val headerID = "${id}-${index}-heading" + val collapseID = "${id}-${index}-collapse" + div("card") { + div("card-header") { + this.id = headerID + h5("mb-0") { + button(classes = "btn btn-link collapsed", type = ButtonType.button) { + attributes["data-toggle"] = "collapse" + attributes["data-target"] = "#$collapseID" + attributes["aria-expanded"] = "false" + attributes["aria-controls"] = collapseID + +title + } + } + } + div("collapse") { + this.id = collapseID + attributes["aria-labelledby"] = headerID + attributes["data-parent"] = "#$id" + div("card-body", block = builder) + } + } + } + } + } +} + + +typealias AccordionBuilder = MutableList Unit>> + +fun AccordionBuilder.entry(title: String, builder: DIV.() -> Unit) { + add(title to builder) +} + +fun TagConsumer.accordion(id: String, builder: AccordionBuilder.() -> Unit) { + val list = ArrayList Unit>>().apply(builder) + accordion(id, list) +} + +fun RBuilder.accordion(id: String, elements: List.() -> Unit>>) { + div("container-fluid") { + div("accordion") { + attrs { + this.id = id + } + elements.forEachIndexed { index, (title, builder) -> + val headerID = "${id}-${index}-heading" + val collapseID = "${id}-${index}-collapse" + div("card") { + div("card-header") { + attrs { + this.id = headerID + } + h5("mb-0") { + button(classes = "btn btn-link collapsed", type = ButtonType.button) { + attrs { + attributes["data-toggle"] = "collapse" + attributes["data-target"] = "#$collapseID" + attributes["aria-expanded"] = "false" + attributes["aria-controls"] = collapseID + } + +title + } + } + } + div("collapse") { + attrs { + this.id = collapseID + attributes["aria-labelledby"] = headerID + attributes["data-parent"] = "#$id" + } + div("card-body", block = builder) + } + } + } + } + } +} + +typealias RAccordionBuilder = MutableList.() -> Unit>> + +fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder
.() -> Unit) { + add(title to builder) +} + +fun RBuilder.accordion(id: String, builder: RAccordionBuilder.() -> Unit) { + val list = ArrayList.() -> Unit>>().apply(builder) + accordion(id, list) +} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt new file mode 100644 index 00000000..3b04817b --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/js/react.kt @@ -0,0 +1,18 @@ +package hep.dataforge.js + +import react.RBuilder +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +fun RBuilder.initState(init: () -> T): ReadWriteProperty = + object : ReadWriteProperty { + val pair = react.useState(init) + override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return pair.first + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + pair.second(value) + } + } + diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt new file mode 100644 index 00000000..e283c92b --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ConfigEditorComponent.kt @@ -0,0 +1,280 @@ +package hep.dataforge.vis.editor + +import hep.dataforge.meta.* +import hep.dataforge.meta.descriptors.* +import hep.dataforge.names.Name +import hep.dataforge.names.NameToken +import hep.dataforge.names.plus +import hep.dataforge.values.* +import hep.dataforge.vis.widgetType +import kotlinx.html.ButtonType +import kotlinx.html.InputType +import kotlinx.html.classes +import kotlinx.html.js.onChangeFunction +import kotlinx.html.js.onClickFunction +import org.w3c.dom.Element +import org.w3c.dom.HTMLInputElement +import org.w3c.dom.HTMLSelectElement +import org.w3c.dom.events.Event +import react.RBuilder +import react.RComponent +import react.RProps +import react.dom.* +import react.setState + +interface ConfigEditorProps : RProps { + /** + * Root config object - always non null + */ + var root: Config + + /** + * Full path to the displayed node in [root]. Could be empty + */ + var name: Name + + /** + * Root default + */ + var default: Meta? + + /** + * Root descriptor + */ + var descriptor: NodeDescriptor? + +} + +class ConfigEditorComponent : RComponent() { + + override fun TreeState.init() { + expanded = true + } + + override fun componentDidMount() { + props.root.onChange(this) { name, _, _ -> + if (name == props.name) { + forceUpdate() + } + } + } + + override fun componentWillUnmount() { + props.root.removeListener(this) + } + + private val onClick: (Event) -> Unit = { + setState { + expanded = !expanded + } + } + + private val onValueChange: (Event) -> Unit = { + val value = when (val t = it.target) { + // (it.target as HTMLInputElement).value + is HTMLInputElement -> if (t.type == "checkbox") { + if (t.checked) True else False + } else { + t.value.asValue() + } + is HTMLSelectElement -> t.value.asValue() + else -> error("Unknown event target: $t") + } + try { + props.root.setValue(props.name, value) + } catch (ex: Exception) { + console.error("Can't set config property ${props.name} to $value") + } + } + + private val removeValue: (Event) -> Unit = { + props.root.remove(props.name) + } + + //TODO replace by separate components + private fun RBuilder.valueChooser(value: Value, descriptor: ValueDescriptor?) { + val type = descriptor?.type?.firstOrNull() + when { + type == ValueType.BOOLEAN -> { + input(type = InputType.checkBox, classes = "float-right") { + attrs { + defaultChecked = value.boolean + onChangeFunction = onValueChange + } + } + } + type == ValueType.NUMBER -> input(type = InputType.number, classes = "float-right") { + attrs { + descriptor.attributes["step"].string?.let { + step = it + } + descriptor.attributes["min"].string?.let { + min = it + } + descriptor.attributes["max"].string?.let { + max = it + } + defaultValue = value.string + onChangeFunction = onValueChange + } + } + descriptor?.allowedValues?.isNotEmpty() ?: false -> select("float-right") { + descriptor!!.allowedValues.forEach { + option { + +it.string + } + } + attrs { + multiple = false + onChangeFunction = onValueChange + } + } + descriptor?.widgetType == "color" -> input(type = InputType.color, classes = "float-right") { + attrs { + defaultValue = value.string + onChangeFunction = onValueChange + } + } + else -> input(type = InputType.text, classes = "float-right") { + attrs { + defaultValue = value.string + onChangeFunction = onValueChange + } + } + } + + } + + + override fun RBuilder.render() { + val item = props.root[props.name] + val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) + val defaultItem = props.default?.get(props.name) + val actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem() + val token = props.name.last()?.toString() ?: "Properties" + + when (actualItem) { + is MetaItem.NodeItem -> { + div { + span("tree-caret") { + attrs { + if (state.expanded) { + classes += "tree-caret-down" + } + onClickFunction = onClick + } + } + span("tree-label") { + +token + attrs { + if (item == null) { + classes += "tree-label-inactive" + } + } + } + } + if (state.expanded) { + ul("tree") { + val keys = buildSet { + item?.node?.items?.keys?.let { addAll(it) } + defaultItem?.node?.items?.keys?.let { addAll(it) } + (descriptorItem as? NodeDescriptor)?.items?.keys?.forEach { + add(NameToken(it)) + } + } + + keys.forEach { token -> + li("tree-item") { + child(ConfigEditorComponent::class) { + attrs { + this.root = props.root + this.name = props.name + token + this.default = props.default + this.descriptor = props.descriptor + } + } + } + } + } + } + } + is MetaItem.ValueItem -> { + div { + div("row") { + div("col") { + p("tree-label") { + +token + attrs { + if (item == null) { + classes += "tree-label-inactive" + } + } + } + } + div("col") { + valueChooser(actualItem.value, descriptorItem as? ValueDescriptor) + } + div("col-auto") { + div("dropleft p-0") { + button(classes = "btn btn-outline-primary") { + attrs { + type = ButtonType.button + attributes["data-toggle"] = "dropdown" + attributes["aria-haspopup"] = "true" + attributes["aria-expanded"] = "false" + attributes["data-boundary"] = "viewport" + } + +"\u22ee" + } + div(classes = "dropdown-menu") { + button(classes = "btn btn-outline dropdown-item") { + +"Info" + } + if (item != null) { + button(classes = "btn btn-outline dropdown-item") { + +"""Clear""" + } + attrs { + onClickFunction = removeValue + } + } + + } + } + } + } + } + } + } + } +} + +fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) { + render(this) { + child(ConfigEditorComponent::class) { + attrs { + root = config + name = Name.EMPTY + this.descriptor = descriptor + this.default = default + } + } + } +} + +fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) { + div { + child(ConfigEditorComponent::class) { + attrs { + root = config + name = Name.EMPTY + this.descriptor = descriptor + this.default = default + } + } + } +} + +fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) { + configEditor(obj.config, descriptor ?: obj.descriptor, default) +} diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/MetaViewerComponent.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/MetaViewerComponent.kt new file mode 100644 index 00000000..75e99c7c --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/MetaViewerComponent.kt @@ -0,0 +1,84 @@ +package hep.dataforge.vis.editor + +import hep.dataforge.meta.Meta +import hep.dataforge.meta.MetaItem +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.NameToken +import kotlinx.html.classes +import kotlinx.html.js.onClickFunction +import org.w3c.dom.events.Event +import react.RBuilder +import react.RComponent +import react.RProps +import react.dom.* +import react.setState + +interface MetaViewerProps : RProps { + var name: NameToken + var meta: Meta + var descriptor: NodeDescriptor? +} + +class MetaViewerComponent : RComponent() { + + override fun TreeState.init() { + expanded = false + } + + private val onClick: (Event) -> Unit = { + setState { + expanded = !expanded + } + } + + override fun RBuilder.render() { + div("d-inline-block text-truncate") { + if (props.meta.items.isNotEmpty()) { + span("tree-caret") { + attrs { + if (state.expanded) { + classes += "tree-caret-down" + } + onClickFunction = onClick + } + } + } + label("tree-label") { + +props.name.toString() + } + ul("tree") { + props.meta.items.forEach { (token, item) -> + //val descriptor = props. + li { + when (item) { + is MetaItem.NodeItem -> { + child(MetaViewerComponent::class) { + attrs { + name = token + meta = item.node + descriptor = props.descriptor?.nodes?.get(token.body) + } + } + } + is MetaItem.ValueItem -> { + div("row") { + div("col") { + label("tree-label") { + +token.toString() + } + } + div("col") { + label { + +item.value.toString() + } + } + } + } + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTree.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTree.kt new file mode 100644 index 00000000..64624f3c --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/ObjectTree.kt @@ -0,0 +1,127 @@ +package hep.dataforge.vis.editor + +import hep.dataforge.js.card +import hep.dataforge.js.initState +import hep.dataforge.names.Name +import hep.dataforge.names.plus +import hep.dataforge.names.startsWith +import hep.dataforge.vis.VisualGroup +import hep.dataforge.vis.VisualObject +import hep.dataforge.vis.isEmpty +import kotlinx.html.classes +import kotlinx.html.js.onClickFunction +import org.w3c.dom.Element +import org.w3c.dom.events.Event +import react.* +import react.dom.* + +interface ObjectTreeProps : RProps { + var name: Name + var selected: Name? + var obj: VisualObject + var clickCallback: (Name) -> Unit +} + +interface TreeState : RState { + var expanded: Boolean +} + +private fun RBuilder.objectTree(props: ObjectTreeProps): Unit { + var expanded: Boolean by initState{ props.selected?.startsWith(props.name) ?: false } + + val onClick: (Event) -> Unit = { + expanded = !expanded + } + + fun RBuilder.treeLabel(text: String) { + button(classes = "btn btn-link tree-label p-0") { + +text + attrs { + if (props.name == props.selected) { + classes += "tree-label-selected" + } + onClickFunction = { props.clickCallback(props.name) } + } + } + } + + val token = props.name.last()?.toString() ?: "World" + val obj = props.obj + + //display as node if any child is visible + if (obj is VisualGroup) { + div("d-block text-truncate") { + if (obj.children.any { !it.key.body.startsWith("@") }) { + span("tree-caret") { + attrs { + if (expanded) { + classes += "tree-caret-down" + } + onClickFunction = onClick + } + } + } + treeLabel(token) + } + if (expanded) { + ul("tree") { + obj.children.entries + .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children + .sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true } + .forEach { (childToken, child) -> + li("tree-item") { + child(ObjectTree) { + attrs { + this.name = props.name + childToken + this.obj = child + this.selected = props.selected + this.clickCallback = props.clickCallback + } + } + } + } + } + } + } else { + div("d-block text-truncate") { + span("tree-leaf") {} + treeLabel(token) + } + } +} + +val ObjectTree: FunctionalComponent = functionalComponent { props -> + objectTree(props) +} + +fun Element.renderObjectTree( + visualObject: VisualObject, + clickCallback: (Name) -> Unit = {} +) = render(this) { + card("Object tree") { + child(ObjectTree) { + attrs { + this.name = Name.EMPTY + this.obj = visualObject + this.selected = null + this.clickCallback = clickCallback + } + } + } +} + +fun RBuilder.objectTree( + visualObject: VisualObject, + selected: Name? = null, + clickCallback: (Name) -> Unit = {} +) { + child(ObjectTree) { + attrs { + this.name = Name.EMPTY + this.obj = visualObject + this.selected = selected + this.clickCallback = clickCallback + } + } +} + diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/propertyEditor.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/propertyEditor.kt new file mode 100644 index 00000000..00f928ee --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/propertyEditor.kt @@ -0,0 +1,49 @@ +package hep.dataforge.vis.editor + +import hep.dataforge.js.card +import hep.dataforge.meta.Meta +import hep.dataforge.meta.descriptors.NodeDescriptor +import hep.dataforge.names.Name +import hep.dataforge.names.isEmpty +import hep.dataforge.vis.VisualObject +import org.w3c.dom.Element +import react.RBuilder +import react.dom.li +import react.dom.nav +import react.dom.ol +import react.dom.render +import kotlin.collections.set + +fun RBuilder.visualPropertyEditor( + path: Name, + item: VisualObject, + descriptor: NodeDescriptor? = item.descriptor, + default: Meta? = null +) { + card("Properties") { + if (!path.isEmpty()) { + nav { + attrs { + attributes["aria-label"] = "breadcrumb" + } + ol("breadcrumb") { + path.tokens.forEach { token -> + li("breadcrumb-item") { + +token.toString() + } + } + } + } + } + configEditor(item, descriptor, default) + } +} + +fun Element.visualPropertyEditor( + path: Name, + item: VisualObject, + descriptor: NodeDescriptor? = item.descriptor, + default: Meta? = null +) = render(this) { + this.visualPropertyEditor(path, item, descriptor, default) +} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/valueChoosers.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/valueChoosers.kt new file mode 100644 index 00000000..8cfc4381 --- /dev/null +++ b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/editor/valueChoosers.kt @@ -0,0 +1,11 @@ +package hep.dataforge.vis.editor + +//val TextValueChooser = functionalComponent { +// +// input(type = InputType.number, classes = "float-right") { +// attrs { +// defaultValue = value.string +// onChangeFunction = onValueChange +// } +// } +//} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt deleted file mode 100644 index 171aa86d..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/bootstrap.kt +++ /dev/null @@ -1,61 +0,0 @@ -package hep.dataforge.vis.js.editor - -import kotlinx.html.* -import kotlinx.html.js.div -import org.w3c.dom.HTMLElement - -inline fun TagConsumer.card(title: String, crossinline block: TagConsumer.() -> Unit) { - div("card w-100") { - div("card-body") { - h3(classes = "card-title") { +title } - block() - } - } -} - -fun TagConsumer.accordion(id: String, elements: Map Unit>) { - div("container-fluid") { - div("accordion") { - this.id = id - elements.entries.forEachIndexed { index, (title, builder) -> - val headerID = "${id}-${index}-heading" - val collapseID = "${id}-${index}-collapse" - div("card") { - div("card-header") { - this.id = headerID - h5("mb-0") { - button(classes = "btn btn-link collapsed", type = ButtonType.button) { - attributes["data-toggle"] = "collapse" - attributes["data-target"] = "#$collapseID" - attributes["aria-expanded"] = "false" - attributes["aria-controls"] = collapseID - +title - } - } - } - div("collapse") { - this.id = collapseID - attributes["aria-labelledby"] = headerID - attributes["data-parent"] = "#$id" - div("card-body", block = builder) - } - } - } - } - } -} - -class AccordionBuilder { - private val map = HashMap Unit>() - fun entry(title: String, block: DIV.() -> Unit) { - map[title] = block - } - - fun build(consumer: TagConsumer, id: String) { - consumer.accordion(id, map) - } -} - -fun TagConsumer.accordion(id: String, block: AccordionBuilder.() -> Unit) { - AccordionBuilder().apply(block).build(this, id) -} \ No newline at end of file diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt deleted file mode 100644 index dde67544..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsTree.kt +++ /dev/null @@ -1,77 +0,0 @@ -package hep.dataforge.vis.js.editor - -import hep.dataforge.names.Name -import hep.dataforge.names.plus -import hep.dataforge.vis.common.VisualGroup -import hep.dataforge.vis.common.VisualObject -import hep.dataforge.vis.common.isEmpty -import kotlinx.html.TagConsumer -import kotlinx.html.dom.append -import kotlinx.html.js.* -import org.w3c.dom.Element -import org.w3c.dom.HTMLElement -import org.w3c.dom.HTMLSpanElement -import kotlin.dom.clear - -fun Element.displayObjectTree( - obj: VisualObject, - clickCallback: (Name) -> Unit = {} -) { - clear() - append { - card("Object tree") { - subTree(Name.EMPTY, obj, clickCallback) - } - } -} - -private fun TagConsumer.subTree( - name: Name, - obj: VisualObject, - clickCallback: (Name) -> Unit -) { - val token = name.last()?.toString()?:"World" - - //display as node if any child is visible - if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) { - lateinit var toggle: HTMLSpanElement - div("d-inline-block text-truncate") { - toggle = span("objTree-caret") - label("objTree-label") { - +token - onClickFunction = { clickCallback(name) } - } - } - val subtree = ul("objTree-subtree") - toggle.onclick = { - toggle.classList.toggle("objTree-caret-down") - subtree.apply { - //If expanded, add children dynamically - if (toggle.classList.contains("objTree-caret-down")) { - obj.children.entries - .filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children - .sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true } - .forEach { (childToken, child) -> - append { - li().apply { - subTree(name + childToken, child, clickCallback) - } - } - } - } else { - // if not, clear them to conserve memory on very long lists - this.clear() - } - } - } - } else { - div("d-inline-block text-truncate") { - span("objTree-leaf") - label("objTree-label") { - +token - onClickFunction = { clickCallback(name) } - } - } - } -} - diff --git a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsoneditor.kt b/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsoneditor.kt deleted file mode 100644 index 8bfaf93a..00000000 --- a/dataforge-vis-common/src/jsMain/kotlin/hep/dataforge/vis/js/editor/jsoneditor.kt +++ /dev/null @@ -1,185 +0,0 @@ -@file:Suppress( - "INTERFACE_WITH_SUPERCLASS", - "OVERRIDING_FINAL_MEMBER", - "RETURN_TYPE_MISMATCH_ON_OVERRIDE", - "CONFLICTING_OVERLOADS", - "EXTERNAL_DELEGATION" -) - -package hep.dataforge.vis.js.editor - -import org.w3c.dom.HTMLElement - -external interface Node { - var field: String - var value: String? get() = definedExternally; set(value) = definedExternally - var path: dynamic -} - -external interface NodeName { - var path: Array - var type: dynamic /* 'object' | 'array' */ - var size: Number -} - -external interface ValidationError { - var path: dynamic - var message: String -} - -external interface Template { - var text: String - var title: String - var className: String? get() = definedExternally; set(value) = definedExternally - var field: String - var value: Any -} - -external interface `T$6` { - var startFrom: Number - var options: Array -} - -external interface AutoCompleteOptions { - var confirmKeys: Array? get() = definedExternally; set(value) = definedExternally - var caseSensitive: Boolean? get() = definedExternally; set(value) = definedExternally -// var getOptions: AutoCompleteOptionsGetter? get() = definedExternally; set(value) = definedExternally -} - -external interface SelectionPosition { - var row: Number - var column: Number -} - -external interface SerializableNode { - var value: Any - var path: dynamic -} - -external interface Color { - var rgba: Array - var hsla: Array - var rgbString: String - var rgbaString: String - var hslString: String - var hslaString: String - var hex: String -} - -//external interface `T$0` { -// var field: Boolean -// var value: Boolean -//} -// -//external interface `T$1` { -// @nativeGetter -// operator fun get(key: String): String? -// -// @nativeSetter -// operator fun set(key: String, value: String) -//} - -//external interface Languages { -// @nativeGetter -// operator fun get(lang: String): `T$1`? -// -// @nativeSetter -// operator fun set(lang: String, value: `T$1`) -//} - -external interface JSONEditorOptions { -// var ace: AceAjax.Ace? get() = definedExternally; set(value) = definedExternally -// var ajv: Ajv? get() = definedExternally; set(value) = definedExternally - var onChange: (() -> Unit)? get() = definedExternally; set(value) = definedExternally - var onChangeJSON: ((json: Any) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onChangeText: ((jsonString: String) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onEditable: ((node: Node) -> dynamic)? get() = definedExternally; set(value) = definedExternally - var onError: ((error: Error) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onModeChange: ((newMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */, oldMode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */) -> Unit)? get() = definedExternally; set(value) = definedExternally - var onNodeName: ((nodeName: NodeName) -> String?)? get() = definedExternally; set(value) = definedExternally - var onValidate: ((json: Any) -> dynamic)? get() = definedExternally; set(value) = definedExternally - var escapeUnicode: Boolean? get() = definedExternally; set(value) = definedExternally - var sortObjectKeys: Boolean? get() = definedExternally; set(value) = definedExternally - var history: Boolean? get() = definedExternally; set(value) = definedExternally - var mode: dynamic /* 'tree' | 'view' | 'form' | 'code' | 'text' */ - var modes: Array? get() = definedExternally; set(value) = definedExternally - var name: String? get() = definedExternally; set(value) = definedExternally - var schema: Any? get() = definedExternally; set(value) = definedExternally - var schemaRefs: Any? get() = definedExternally; set(value) = definedExternally - var search: Boolean? get() = definedExternally; set(value) = definedExternally - var indentation: Number? get() = definedExternally; set(value) = definedExternally - var theme: String? get() = definedExternally; set(value) = definedExternally - var templates: Array