Working tree navigation

This commit is contained in:
Alexander Nozik 2019-09-25 19:29:49 +03:00
parent 66caebb090
commit 4fa2e50777
18 changed files with 801 additions and 151 deletions

View File

@ -2,6 +2,7 @@ package hep.dataforge.vis.common
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.TYPE import hep.dataforge.vis.common.VisualObject.Companion.TYPE
import kotlinx.serialization.Transient import kotlinx.serialization.Transient
@ -59,3 +60,6 @@ interface VisualObject : MetaRepr, Configurable {
} }
} }
fun VisualObject.getProperty(key: String, inherit: Boolean = true): MetaItem<*>? = getProperty(key.toName(), inherit)
fun VisualObject.setProperty(key: String, value: Any?) = setProperty(key.toName(), value)

View File

@ -17,6 +17,7 @@ kotlin {
val jsMain by getting { val jsMain by getting {
dependencies { dependencies {
api(project(":dataforge-vis-spatial")) api(project(":dataforge-vis-spatial"))
api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
} }
} }
} }

View File

@ -1,6 +1,10 @@
package hep.dataforge.vis.spatial.gdml.demo package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.hmr.ApplicationBase import hep.dataforge.vis.hmr.ApplicationBase
import hep.dataforge.vis.hmr.startApplication import hep.dataforge.vis.hmr.startApplication
import hep.dataforge.vis.spatial.* import hep.dataforge.vis.spatial.*
@ -10,16 +14,14 @@ import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreeOutput import hep.dataforge.vis.spatial.three.ThreeOutput
import hep.dataforge.vis.spatial.three.ThreePlugin import hep.dataforge.vis.spatial.three.ThreePlugin
import hep.dataforge.vis.spatial.three.output import hep.dataforge.vis.spatial.three.output
import kotlinx.coroutines.CoroutineScope import hep.dataforge.vis.spatial.tree.render
import kotlinx.coroutines.GlobalScope import hep.dataforge.vis.spatial.tree.toTree
import kotlinx.coroutines.launch
import kotlinx.html.InputType import kotlinx.html.InputType
import kotlinx.html.dom.append import kotlinx.html.dom.append
import kotlinx.html.js.input import kotlinx.html.js.*
import kotlinx.html.js.li
import kotlinx.html.js.ul
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLDivElement import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
import org.w3c.files.FileList import org.w3c.files.FileList
import org.w3c.files.FileReader import org.w3c.files.FileReader
@ -42,7 +44,7 @@ private class GDMLDemoApp : ApplicationBase() {
/** /**
* Load data from text file * Load data from text file
*/ */
private fun loadData(event: Event, block: suspend CoroutineScope.(name: String, data: String) -> Unit) { private fun loadData(event: Event, block: (name: String, data: String) -> Unit) {
event.stopPropagation() event.stopPropagation()
event.preventDefault() event.preventDefault()
@ -53,34 +55,46 @@ private class GDMLDemoApp : ApplicationBase() {
FileReader().apply { FileReader().apply {
onload = { onload = {
val string = result as String val string = result as String
GlobalScope.launch {
try {
block(file.name, string) block(file.name, string)
} catch (ex: Exception) {
console.error(ex)
} }
} }
readAsText(file) readAsText(file)
} }
} }
private fun spinner(show: Boolean) { private fun spinner(show: Boolean) {
val style = if (show) { // if( show){
"display:block;" //
} else { // val style = if (show) {
"display:none;" // "display:block;"
} // } else {
document.getElementById("loader")?.setAttribute("style", style) // "display:none;"
// }
// document.getElementById("canvas")?.append {
//
// }
} }
private fun message(message: String?) { private fun message(message: String?) {
val element = document.getElementById("message") document.getElementById("messages")?.let { element ->
if (message == null) { if (message == null) {
element?.setAttribute("style", "display:none;") element.clear()
} else { } else {
element?.textContent = message element.append {
element?.setAttribute("style", "display:block;") p {
+message
}
}
}
} }
} }
fun setupSidebar(element: Element, output: ThreeOutput, root: VisualObject3D) { fun setupLayers(element: Element, output: ThreeOutput) {
element.clear() element.clear()
element.append { element.append {
ul("list-group") { ul("list-group") {
@ -103,7 +117,6 @@ private class GDMLDemoApp : ApplicationBase() {
} }
} }
} }
element.appendFancyTree(root)
} }
private val gdmlConfiguration: GDMLTransformer.() -> Unit = { private val gdmlConfiguration: GDMLTransformer.() -> Unit = {
@ -137,6 +150,43 @@ private class GDMLDemoApp : ApplicationBase() {
} }
fun showEditor(item: VisualObject?, name: String?) {
val element = document.getElementById("editor") ?: error("Element with id 'canvas' not found on page")
element.clear()
if (item != null) {
element.append {
div("card") {
div("card-body") {
h3(classes = "card-title") { +(name ?: "") }
form {
div("form-group") {
label {
+"Color: "
}
input(InputType.color, classes = "form-control").apply {
onchange = { event ->
item.color(value)
}
}
}
div("form-group form-check") {
input(InputType.checkBox, classes = "form-check-input").apply {
this.value = item.material?.get("color").string ?: ""
onchange = { event ->
item.visible = checked
Unit
}
}
label("form-check-label") { +"Visible" }
}
}
}
}
}
}
}
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
val context = Global.context("demo") {} val context = Global.context("demo") {}
@ -144,15 +194,16 @@ private class GDMLDemoApp : ApplicationBase() {
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO") //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val canvas = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page") val canvas = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
val sidebar = document.getElementById("sidebar") ?: error("Element with id 'sidebar' not found on page") val layers = document.getElementById("layers") ?: error("Element with id 'layers' not found on page")
val tree = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
canvas.clear() canvas.clear()
val action: suspend CoroutineScope.(name: String, data: String) -> Unit = { name, data -> val action: (name: String, data: String) -> Unit = { name, data ->
canvas.clear() canvas.clear()
launch { spinner(true) } spinner(true)
launch { message("Loading GDML") } message("Loading GDML")
val gdml = GDML.format.parse(GDML.serializer(), data) val gdml = GDML.format.parse(GDML.serializer(), data)
launch { message("Converting GDML into DF-VIS format") } message("Converting GDML into DF-VIS format")
val visual: VisualObject3D = when { val visual: VisualObject3D = when {
name.endsWith(".gdml") || name.endsWith(".xml") -> gdml.toVisual(gdmlConfiguration) name.endsWith(".gdml") || name.endsWith(".xml") -> gdml.toVisual(gdmlConfiguration)
@ -165,19 +216,22 @@ private class GDMLDemoApp : ApplicationBase() {
} }
} }
launch { message("Rendering") } message("Rendering")
val output = three.output(canvas) val output = three.output(canvas as HTMLElement)
//output.camera.layers.enable(1) //output.camera.layers.enable(1)
output.camera.layers.set(0) output.camera.layers.set(0)
setupSidebar(sidebar, output, visual) setupLayers(layers, output)
if (visual is VisualGroup) {
visual.toTree(::showEditor).render(tree as HTMLElement) {
showCheckboxes = true
}
}
output.render(visual) output.render(visual)
launch { message(null)
message(null) spinner(false)
spinner(false)
}
} }
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply { (document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
@ -187,6 +241,9 @@ private class GDMLDemoApp : ApplicationBase() {
} }
override fun dispose(): Map<String, Any> {
return super.dispose()
}
} }
fun main() { fun main() {

View File

@ -1,13 +0,0 @@
package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.tree.JSVisualTree
import nl.adaptivity.js.util.asElement
import org.w3c.dom.Element
fun Element.appendFancyTree(root: VisualObject3D){
val visualTree = JSVisualTree("world", root) {}
val treeNode = visualTree.root
treeNode.asElement()!!.id = "fancytree"
appendChild(treeNode)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -4,11 +4,13 @@
<meta charset="utf-8"> <meta charset="utf-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">--> <!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
<title>Three js demo for particle physics</title> <title>Three js demo for particle physics</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/jquery.fancytree@2.32.0/dist/modules/jquery.fancytree.min.js"></script> <link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery.fancytree@2.32.0/dist/skin-bootstrap/ui.fancytree.min.css"> <link rel="stylesheet" href="css/inspire-tree-light.min.css">
<link rel="stylesheet" href="main.css">
<script type="text/javascript" src="main.bundle.js"></script> <script type="text/javascript" src="main.bundle.js"></script>
</head> </head>
<body class="testApp"> <body class="testApp">
@ -21,18 +23,53 @@
<div class="container"> <div class="container">
<h1>GDML demo</h1> <h1>GDML demo</h1>
</div> </div>
<div class="container loader" id="loader" style="display:none;"></div> <!--<div class="container loader" id="loader" style="display:none;"></div>-->
<div class="container animate-bottom" id="message" style="display:none;"></div> <div class="container animate-bottom" id="messages"></div>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-9" id="canvas"></div> <div class="col-9" id="canvas"></div>
<div class="col-3" id="sidebar"></div> <div class="col-3">
<div id="editor"></div>
<div class="accordion" id="accordion">
<div class="card">
<div class="card-header" id="layers-header">
<h2 class="mb-0">
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#layers-body"
aria-expanded="true" aria-controls="layers-body">
Layers
</button>
</h2>
</div>
<div id="layers-body" class="collapse show" aria-labelledby="layers-header"
data-parent="#accordion">
<div class="card-body">
<div id="layers"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-header" id="tree-header">
<h2 class="mb-0">
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
data-target="#tree-body" aria-expanded="false" aria-controls="tree-body">
Object tree
</button>
</h2>
</div>
<div id="tree-body" class="collapse" aria-labelledby="tree-header" data-parent="#accordion">
<div class="card-body">
<div id="tree"></div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>

View File

@ -5,12 +5,12 @@ plugins {
id("org.openjfx.javafxplugin") id("org.openjfx.javafxplugin")
} }
scientifik{ scientifik {
withSerialization() withSerialization()
} }
kotlin { kotlin {
jvm{ jvm {
withJava() withJava()
} }
sourceSets { sourceSets {
@ -19,20 +19,20 @@ kotlin {
api(project(":dataforge-vis-common")) api(project(":dataforge-vis-common"))
} }
} }
jvmMain{ jvmMain {
dependencies { dependencies {
implementation(project(":dataforge-vis-fx")) implementation(project(":dataforge-vis-fx"))
implementation("org.fxyz3d:fxyz3d:0.4.0") implementation("org.fxyz3d:fxyz3d:0.4.0")
} }
} }
jsMain{ jsMain {
dependencies { dependencies {
implementation(npm("three", "0.106.2")) implementation(npm("three", "0.106.2"))
implementation(npm("@hi-level/three-csg", "1.0.6")) implementation(npm("@hi-level/three-csg", "1.0.6"))
implementation(npm("style-loader")) implementation(npm("style-loader"))
implementation(npm("element-resize-event")) implementation(npm("element-resize-event"))
// api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0") implementation(npm("inspire-tree","6.0.1"))
// implementation(npm("jquery.fancytree","2.32.0")) implementation(npm("inspire-tree-dom","4.0.6"))
} }
} }
} }

View File

@ -11,6 +11,7 @@ import hep.dataforge.vis.common.asName
import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.DETAIL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.LAYER_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.MATERIAL_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.SELECTED_KEY
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
import kotlinx.serialization.UseSerializers import kotlinx.serialization.UseSerializers
@ -34,6 +35,7 @@ interface VisualObject3D : VisualObject {
companion object { companion object {
val MATERIAL_KEY = "material".asName() val MATERIAL_KEY = "material".asName()
val VISIBLE_KEY = "visible".asName() val VISIBLE_KEY = "visible".asName()
val SELECTED_KEY = "selected".asName()
val DETAIL_KEY = "detail".asName() val DETAIL_KEY = "detail".asName()
val LAYER_KEY = "layer".asName() val LAYER_KEY = "layer".asName()
@ -117,10 +119,18 @@ var VisualObject.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean get() = getProperty(VISIBLE_KEY).boolean
set(value) = setProperty(VISIBLE_KEY, value) set(value) = setProperty(VISIBLE_KEY, value)
var VisualObject.selected: Boolean?
get() = getProperty(SELECTED_KEY).boolean
set(value) = setProperty(SELECTED_KEY, value)
fun VisualObject.color(rgb: Int) { fun VisualObject.color(rgb: Int) {
material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb } material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb }
} }
fun VisualObject.color(rgb: String) {
material = (material?.builder() ?: MetaBuilder()).apply { "color" to rgb }
}
fun VisualObject3D.material(builder: MetaBuilder.() -> Unit) { fun VisualObject3D.material(builder: MetaBuilder.() -> Unit) {
material = buildMeta(builder) material = buildMeta(builder)
} }

View File

@ -1,8 +0,0 @@
package hep.dataforge.vis.spatial.tree
import hep.dataforge.vis.common.VisualObject
interface VisualTree<out T: VisualObject> {
val item: T?
val children: Collection<VisualTree<T>>
}

View File

@ -10,8 +10,9 @@ import info.laht.threekt.WebGLRenderer
import info.laht.threekt.helpers.AxesHelper import info.laht.threekt.helpers.AxesHelper
import info.laht.threekt.lights.AmbientLight import info.laht.threekt.lights.AmbientLight
import info.laht.threekt.scenes.Scene import info.laht.threekt.scenes.Scene
import org.w3c.dom.Element import org.w3c.dom.HTMLElement
import kotlin.browser.window import kotlin.browser.window
import kotlin.dom.clear
private val elementResizeEvent = require("element-resize-event") private val elementResizeEvent = require("element-resize-event")
@ -23,7 +24,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
val scene: Scene = Scene().apply { val scene: Scene = Scene().apply {
add(AmbientLight()) add(AmbientLight())
if(meta["axes.visible"].boolean == true){ if (meta["axes.visible"].boolean == true) {
axes.visible = true axes.visible = true
} }
add(axes) add(axes)
@ -31,7 +32,8 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta) val camera = three.buildCamera(meta["camera"].node ?: EmptyMeta)
fun attach(element: Element, computeWidth: Element.() -> Int = { element.clientWidth }) { fun attach(element: HTMLElement, computeWidth: HTMLElement.() -> Int = { this.offsetWidth }) {
element.clear()
val width by meta.number(computeWidth(element)).int val width by meta.number(computeWidth(element)).int
val height: Int = (width / camera.aspect).toInt() val height: Int = (width / camera.aspect).toInt()
@ -56,7 +58,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
renderer.setSize(newWidth, (newWidth / camera.aspect).toInt()) renderer.setSize(newWidth, (newWidth / camera.aspect).toInt())
} }
element.replaceWith(renderer.domElement) element.appendChild(renderer.domElement)
animate() animate()
} }
@ -65,7 +67,7 @@ class ThreeOutput(val three: ThreePlugin, val meta: Meta = EmptyMeta) : Output<V
} }
} }
fun ThreePlugin.output(element: Element? = null, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) = fun ThreePlugin.output(element: HTMLElement? = null, meta: Meta = EmptyMeta, override: MetaBuilder.() -> Unit = {}) =
ThreeOutput(this, buildMeta(meta, override)).apply { ThreeOutput(this, buildMeta(meta, override)).apply {
if (element != null) { if (element != null) {
attach(element) attach(element)

View File

@ -1,74 +0,0 @@
package hep.dataforge.vis.spatial.tree
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.spatial.visible
import kotlinx.html.InputType
import kotlinx.html.dom.append
import kotlinx.html.js.*
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.Node
import kotlin.browser.document
class JSVisualTree(
val title: String,
override val item: VisualObject,
val callback: VisualObject.() -> Unit
) : VisualTree<VisualObject> {
override val children: Collection<JSVisualTree> by lazy {
if (item is VisualGroup) {
item.children.mapNotNull {(key,value)->
val str = key.toString()
if(!str.startsWith("@")) {
JSVisualTree(str, value, callback)
} else {
null
}
}
} else {
emptyList()
}
}
var visible: Boolean
get() = item.visible ?: true
set(value) {
item.visible = value
}
val root: Node by lazy {
(document.createElement("div") as HTMLDivElement).apply {
append {
div(TREE_ITEM_HEADER_CLASS) {
input(type = InputType.checkBox).apply {
checked = visible
onChangeFunction = {
visible = checked
}
}
a {
+this@JSVisualTree.title
+"[${item::class}]"
}
}
if (item is VisualGroup) {
ul {
this@JSVisualTree.children.forEach { value ->
li(TREE_NODE_CLASS).apply {
appendChild(value.root)
}
}
}
}
}
}
}
companion object {
const val TREE_NODE_CLASS = "tree-node"
const val TREE_LEAF_CLASS = "tree-leaf"
const val TREE_ITEM_HEADER_CLASS = "tree-header"
}
}

View File

@ -0,0 +1,65 @@
@file:Suppress(
"INTERFACE_WITH_SUPERCLASS",
"OVERRIDING_FINAL_MEMBER",
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
"CONFLICTING_OVERLOADS",
"EXTERNAL_DELEGATION"
)
@file:JsModule("eventemitter2")
@file: JsNonModule
package hep.dataforge.vis.spatial.tree
import kotlin.js.Promise
//typealias eventNS = Array<String>
external interface ConstructorOptions {
var wildcard: Boolean? get() = definedExternally; set(value) = definedExternally
var delimiter: String? get() = definedExternally; set(value) = definedExternally
var newListener: Boolean? get() = definedExternally; set(value) = definedExternally
var maxListeners: Number? get() = definedExternally; set(value) = definedExternally
var verboseMemoryLeak: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface EventAndListener {
@nativeInvoke
operator fun invoke(event: String, vararg values: Any)
@nativeInvoke
operator fun invoke(event: Array<String>, vararg values: Any)
}
external open class EventEmitter2(options: ConstructorOptions? = definedExternally /* null */) {
open fun emit(event: String, vararg values: Any): Boolean
open fun emit(event: Array<String>, vararg values: Any): Boolean
open fun emitAsync(event: String, vararg values: Any): Promise<Array<Any>>
open fun emitAsync(event: Array<String>, vararg values: Any): Promise<Array<Any>>
open fun addListener(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun on(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun on(event: Array<String>, listener: Function<*>): EventEmitter2 /* this */
open fun prependListener(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun prependListener(event: Array<String>, listener: Function<*>): EventEmitter2 /* this */
open fun once(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun once(event: Array<String>, listener: Function<*>): EventEmitter2 /* this */
open fun prependOnceListener(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun prependOnceListener(event: Array<String>, listener: Function<*>): EventEmitter2 /* this */
open fun many(event: String, timesToListen: Number, listener: Function<*>): EventEmitter2 /* this */
open fun many(event: Array<String>, timesToListen: Number, listener: Function<*>): EventEmitter2 /* this */
open fun prependMany(event: String, timesToListen: Number, listener: Function<*>): EventEmitter2 /* this */
open fun prependMany(event: Array<String>, timesToListen: Number, listener: Function<*>): EventEmitter2 /* this */
open fun onAny(listener: EventAndListener): EventEmitter2 /* this */
open fun prependAny(listener: EventAndListener): EventEmitter2 /* this */
open fun offAny(listener: Function<*>): EventEmitter2 /* this */
open fun removeListener(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun removeListener(event: Array<String>, listener: Function<*>): EventEmitter2 /* this */
open fun off(event: String, listener: Function<*>): EventEmitter2 /* this */
open fun removeAllListeners(event: String? = definedExternally /* null */): EventEmitter2 /* this */
open fun removeAllListeners(event: Array<String>? = definedExternally /* null */): EventEmitter2 /* this */
open fun setMaxListeners(n: Number)
open fun eventNames(): Array<String>
open fun listeners(event: String): Array<Function<*>>
open fun listeners(event: Array<String>): Array<Function<*>>
open fun listenersAny(): Array<Function<*>>
open fun removeAllListeners(): EventEmitter2 /* this */
}

View File

@ -0,0 +1,38 @@
@file:Suppress(
"INTERFACE_WITH_SUPERCLASS",
"OVERRIDING_FINAL_MEMBER",
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
"CONFLICTING_OVERLOADS",
"EXTERNAL_DELEGATION"
)
package hep.dataforge.vis.spatial.tree
import org.w3c.dom.HTMLElement
external interface DropTargetValidator {
@nativeInvoke
operator fun invoke(dragNode: TreeNode, targetNode: TreeNode): Boolean
}
external interface DragAndDrop {
var enabled: Boolean? get() = definedExternally; set(value) = definedExternally
var validateOn: String? get() = definedExternally; set(value) = definedExternally
var validate: DropTargetValidator
}
external interface DomConfig {
var autoLoadMore: Boolean? get() = definedExternally; set(value) = definedExternally
var deferredRendering: Boolean? get() = definedExternally; set(value) = definedExternally
var dragAndDrop: DragAndDrop? get() = definedExternally; set(value) = definedExternally
var nodeHeight: Number? get() = definedExternally; set(value) = definedExternally
var showCheckboxes: Boolean? get() = definedExternally; set(value) = definedExternally
var dragTargets: Array<String>? get() = definedExternally; set(value) = definedExternally
var tabindex: Number? get() = definedExternally; set(value) = definedExternally
var target: HTMLElement
}
@JsModule("inspire-tree-dom")
@JsNonModule
open external class InspireTreeDOM(tree: Any, opts: DomConfig)

View File

@ -0,0 +1,417 @@
@file:Suppress(
"INTERFACE_WITH_SUPERCLASS",
"OVERRIDING_FINAL_MEMBER",
"RETURN_TYPE_MISMATCH_ON_OVERRIDE",
"CONFLICTING_OVERLOADS",
"EXTERNAL_DELEGATION",
"unused"
)
package hep.dataforge.vis.spatial.tree
import kotlin.js.Promise
import kotlin.js.RegExp
external interface NodeIteratee {
@nativeInvoke
operator fun invoke(node: TreeNode): Any
}
external interface MatchProcessor {
@nativeInvoke
operator fun invoke(matches: TreeNodes): Any
}
external interface SearchMatcher {
@nativeInvoke
operator fun invoke(query: String, resolve: Any, reject: Any): Any
}
external interface `T$0` {
var add: Boolean? get() = definedExternally; set(value) = definedExternally
var edit: Boolean? get() = definedExternally; set(value) = definedExternally
var remove: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface `T$1` {
var resetStateOnRestore: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface `T$2` {
var limit: Number? get() = definedExternally; set(value) = definedExternally
}
external interface `T$3` {
var matcher: SearchMatcher
var matchProcess: MatchProcessor
}
external interface Selection {
var allow: NodeIteratee? get() = definedExternally; set(value) = definedExternally
var autoDeselect: Boolean? get() = definedExternally; set(value) = definedExternally
var autoSelectChildren: Boolean? get() = definedExternally; set(value) = definedExternally
var disableDirectDeselection: Boolean? get() = definedExternally; set(value) = definedExternally
var mode: String? get() = definedExternally; set(value) = definedExternally
var multiple: Boolean? get() = definedExternally; set(value) = definedExternally
var require: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface Config {
var allowLoadEvents: Array<String>? get() = definedExternally; set(value) = definedExternally
// var checkbox: dynamic get() = definedExternally; set(value) = definedExternally
var contextMenu: Boolean? get() = definedExternally; set(value) = definedExternally
val data: ((node: TreeNode, resolve: (nodes: Array<NodeConfig>) -> Any, reject: (err: Error) -> Any) -> dynamic)? get() = definedExternally
var deferredLoading: Boolean? get() = definedExternally; set(value) = definedExternally
var editable: Boolean? get() = definedExternally; set(value) = definedExternally
var editing: `T$0`? get() = definedExternally; set(value) = definedExternally
var nodes: `T$1`? get() = definedExternally; set(value) = definedExternally
var pagination: `T$2`? get() = definedExternally; set(value) = definedExternally
var renderer: Any? get() = definedExternally; set(value) = definedExternally
var search: `T$3`? get() = definedExternally; set(value) = definedExternally
var selection: Selection? get() = definedExternally; set(value) = definedExternally
var sort: String? get() = definedExternally; set(value) = definedExternally
var multiselect: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface State {
var checked: Boolean? get() = definedExternally; set(value) = definedExternally
var collapsed: Boolean? get() = definedExternally; set(value) = definedExternally
var draggable: Boolean? get() = definedExternally; set(value) = definedExternally
//var `drop-target`: Boolean? get() = definedExternally; set(value) = definedExternally
var editable: Boolean? get() = definedExternally; set(value) = definedExternally
var focused: Boolean? get() = definedExternally; set(value) = definedExternally
var hidden: Boolean? get() = definedExternally; set(value) = definedExternally
var indeterminate: Boolean? get() = definedExternally; set(value) = definedExternally
var loading: Boolean? get() = definedExternally; set(value) = definedExternally
var matched: Boolean? get() = definedExternally; set(value) = definedExternally
var removed: Boolean? get() = definedExternally; set(value) = definedExternally
var rendered: Boolean? get() = definedExternally; set(value) = definedExternally
var selectable: Boolean? get() = definedExternally; set(value) = definedExternally
var selected: Boolean? get() = definedExternally; set(value) = definedExternally
}
external interface InspireTag {
var attributes: Any? get() = definedExternally; set(value) = definedExternally
}
external interface ITree {
var a: InspireTag? get() = definedExternally; set(value) = definedExternally
var icon: String? get() = definedExternally; set(value) = definedExternally
var li: InspireTag? get() = definedExternally; set(value) = definedExternally
var state: State? get() = definedExternally; set(value) = definedExternally
}
external interface NodeConfig {
var children: dynamic /* Array<NodeConfig> | true */
var id: String? get() = definedExternally; set(value) = definedExternally
var text: String
@JsName("itree")
var itree: ITree? get() = definedExternally; set(value) = definedExternally
}
external interface Pagination {
var limit: Number
var total: Number
}
@JsModule("inspire-tree")
@JsNonModule
open external class InspireTree(opts: Config) : EventEmitter2 {
constructor(tree: InspireTree)
constructor(tree: InspireTree, array: Array<Any>)
constructor(tree: InspireTree, array: TreeNodes)
open fun addNode(node: NodeConfig): TreeNode
open fun addNodes(node: Array<NodeConfig>): TreeNodes
open fun available(): TreeNodes
open fun blur(): TreeNodes
open fun blurDeep(): TreeNodes
open fun boundingNodes(): Array<TreeNode>
open fun canAutoDeselect(): Boolean
open fun checked(): TreeNodes
open fun clean(): TreeNodes
open fun clearSearch(): InspireTree
open fun clone(): TreeNodes
open fun collapse(): TreeNodes
open fun collapsed(full: Boolean? = definedExternally /* null */): TreeNodes
open fun collapseDeep(): TreeNodes
open fun context(): TreeNode
open fun copy(
dest: InspireTree,
hierarchy: Boolean? = definedExternally /* null */,
includeState: Boolean? = definedExternally /* null */
): TreeNodes
open fun createNode(obj: Any): TreeNode
open fun deepest(): TreeNodes
open fun deselect(): TreeNodes
open fun deselectDeep(): TreeNodes
open fun disableDeselection(): InspireTree
open fun each(iteratee: NodeIteratee): TreeNodes
open fun editable(full: Boolean? = definedExternally /* null */): TreeNodes
open fun editing(full: Boolean? = definedExternally /* null */): TreeNodes
open fun enableDeselection(): InspireTree
open fun expand(): Promise<TreeNodes>
open fun expandDeep(): TreeNodes
open fun expanded(full: Boolean? = definedExternally /* null */): TreeNodes
open fun expandParents(): TreeNodes
open fun extract(predicate: String): TreeNodes
open fun extract(predicate: NodeIteratee): TreeNodes
open fun filterBy(predicate: String): TreeNodes
open fun filterBy(predicate: NodeIteratee): TreeNodes
open fun find(
predicate: (node: TreeNode, index: Number? /* = null */, obj: Array<TreeNode>? /* = null */) -> Boolean,
thisArg: Any? = definedExternally /* null */
): TreeNode
open fun first(predicate: (node: TreeNode) -> Boolean): TreeNode
open fun flatten(predicate: String): TreeNodes
open fun flatten(predicate: NodeIteratee): TreeNodes
open fun focused(full: Boolean? = definedExternally /* null */): TreeNodes
open fun get(index: Number): TreeNode
open fun hidden(full: Boolean? = definedExternally /* null */): TreeNodes
open fun hide(): TreeNodes
open fun hideDeep(): TreeNodes
open var id: dynamic /* String | Number */
open var config: Config
open var preventDeselection: Boolean
open fun indeterminate(full: Boolean? = definedExternally /* null */): TreeNodes
open fun insertAt(index: Number, `object`: Any): TreeNode
open fun invoke(methods: String): TreeNodes
open fun invoke(methods: Array<String>): TreeNodes
open fun invokeDeep(methods: String): TreeNodes
open fun invokeDeep(methods: Array<String>): TreeNodes
open fun isEventMuted(eventName: String): Boolean
open fun last(predicate: (node: TreeNode) -> Boolean): TreeNode
open fun lastSelectedNode(): TreeNode
open fun load(loader: Promise<TreeNodes>): Promise<TreeNodes>
open fun loading(full: Boolean? = definedExternally /* null */): TreeNodes
open fun matched(full: Boolean? = definedExternally /* null */): TreeNodes
open fun move(index: Number, newIndex: Number, target: TreeNodes): TreeNode
open fun mute(events: Array<String>): InspireTree
open fun muted(): Boolean
open fun node(id: String): TreeNode
open fun node(id: Number): TreeNode
open fun nodes(ids: Array<String>? = definedExternally /* null */): TreeNodes
open fun nodes(ids: Array<Number>? = definedExternally /* null */): TreeNodes
open fun pagination(): Pagination
open fun recurseDown(iteratee: NodeIteratee): TreeNodes
open fun reload(): Promise<TreeNodes>
open fun removeAll(): InspireTree
open fun removed(full: Boolean? = definedExternally /* null */): TreeNodes
open fun restore(): TreeNodes
open fun restoreDeep(): TreeNodes
open fun search(query: String): Promise<TreeNodes>
open fun search(query: RegExp): Promise<TreeNodes>
open fun search(query: NodeIteratee): Promise<TreeNodes>
open fun select(): TreeNodes
open fun selectable(full: Boolean? = definedExternally /* null */): TreeNodes
open fun selectBetween(start: TreeNode, end: TreeNode): InspireTree
open fun selectDeep(): TreeNodes
open fun selected(full: Boolean? = definedExternally /* null */): TreeNodes
open fun selectFirstAvailableNode(): TreeNode
open fun show(): TreeNodes
open fun showDeep(): TreeNodes
open fun softRemove(): TreeNodes
open fun sortBy(sorter: String): TreeNodes
open fun sortBy(sorter: NodeIteratee): TreeNodes
open fun state(key: String, `val`: Boolean): TreeNodes
open fun stateDeep(key: String, `val`: Boolean): TreeNodes
open fun swap(node1: TreeNode, node2: TreeNode): TreeNodes
open fun toArray(): Array<Any>
open fun toArray(): Array<Any>
open fun tree(): InspireTree
open fun unmute(events: Array<String>): InspireTree
open fun visible(full: Boolean? = definedExternally /* null */): TreeNodes
open fun nodes(): TreeNodes
companion object {
fun isTreeNode(`object`: Any): Boolean
fun isTreeNodes(`object`: Any): Boolean
}
}
open external class TreeNodes(tree: InspireTree) {
constructor(tree: InspireTree, array: Array<Any>)
constructor(tree: InspireTree, array: TreeNodes)
open fun addNode(node: NodeConfig): TreeNode
open fun available(): TreeNodes
open fun blur(): TreeNodes
open fun blurDeep(): TreeNodes
open fun checked(): TreeNodes
open fun clean(): TreeNodes
open fun clone(): TreeNodes
open fun collapse(): TreeNodes
open fun collapsed(full: Boolean? = definedExternally /* null */): TreeNodes
open fun collapseDeep(): TreeNodes
open fun context(): TreeNode
open fun copy(
dest: InspireTree,
hierarchy: Boolean? = definedExternally /* null */,
includeState: Boolean? = definedExternally /* null */
): TreeNodes
open fun deepest(): TreeNodes
open fun deselect(): TreeNodes
open fun deselectDeep(): TreeNodes
open fun each(iteratee: NodeIteratee): TreeNodes
open fun editable(full: Boolean? = definedExternally /* null */): TreeNodes
open fun editing(full: Boolean? = definedExternally /* null */): TreeNodes
open fun expand(): TreeNodes
open fun expandDeep(): Promise<TreeNodes>
open fun expanded(full: Boolean? = definedExternally /* null */): TreeNodes
open fun expandParents(): TreeNodes
open fun extract(predicate: String): TreeNodes
open fun extract(predicate: NodeIteratee): TreeNodes
open fun filterBy(predicate: String): TreeNodes
open fun filterBy(predicate: NodeIteratee): TreeNodes
open fun find(
predicate: (node: TreeNode, index: Number? /* = null */, obj: Array<TreeNode>? /* = null */) -> Boolean,
thisArg: Any? = definedExternally /* null */
): TreeNode
open fun flatten(predicate: String): TreeNodes
open fun flatten(predicate: NodeIteratee): TreeNodes
open fun focused(full: Boolean? = definedExternally /* null */): TreeNodes
operator fun get(index: Number): TreeNode
open fun hidden(full: Boolean? = definedExternally /* null */): TreeNodes
open fun hide(): TreeNodes
open fun hideDeep(): TreeNodes
open fun indeterminate(full: Boolean? = definedExternally /* null */): TreeNodes
open fun insertAt(index: Number, `object`: Any): TreeNode
open fun invoke(methods: String): TreeNodes
open fun invoke(methods: Array<String>): TreeNodes
open fun invokeDeep(methods: String): TreeNodes
open fun invokeDeep(methods: Array<String>): TreeNodes
open fun loading(full: Boolean? = definedExternally /* null */): TreeNodes
open fun matched(full: Boolean? = definedExternally /* null */): TreeNodes
open fun move(index: Number, newIndex: Number, target: TreeNodes): TreeNode
open fun node(id: String): TreeNode
open fun node(id: Number): TreeNode
open fun nodes(ids: Array<String>? = definedExternally /* null */): TreeNodes
open fun nodes(ids: Array<Number>? = definedExternally /* null */): TreeNodes
open fun pagination(): Pagination
open fun recurseDown(iteratee: NodeIteratee): TreeNodes
open fun removed(full: Boolean? = definedExternally /* null */): TreeNodes
open fun restore(): TreeNodes
open fun restoreDeep(): TreeNodes
open fun select(): TreeNodes
open fun selectable(full: Boolean? = definedExternally /* null */): TreeNodes
open fun selectBetween(start: TreeNode, end: TreeNode): InspireTree
open fun selectDeep(): TreeNodes
open fun selected(full: Boolean? = definedExternally /* null */): TreeNodes
open fun show(): TreeNodes
open fun showDeep(): TreeNodes
open fun softRemove(): TreeNodes
open fun sortBy(sorter: String): TreeNodes
open fun sortBy(sorter: NodeIteratee): TreeNodes
open fun state(key: String, `val`: Boolean): TreeNodes
open fun stateDeep(key: String, `val`: Boolean): TreeNodes
open fun swap(node1: TreeNode, node2: TreeNode): TreeNodes
open fun toArray(): Array<Any>
open fun tree(): InspireTree
open fun visible(full: Boolean? = definedExternally /* null */): TreeNodes
open fun nodes(): TreeNodes
}
open external class TreeNode(tree: InspireTree) {
constructor(tree: InspireTree, source: Any, excludeKeys: Array<String>)
constructor(tree: InspireTree, source: TreeNode, excludeKeys: Array<String>)
constructor(tree: InspireTree, source: Any)
constructor(tree: InspireTree, source: TreeNode)
open fun addChild(node: NodeConfig): TreeNode
open fun addChildren(nodes: Array<NodeConfig>): TreeNodes
open fun assign(vararg sources: Any?): TreeNode
open fun available(): Boolean
open fun blur(): TreeNode
open fun check(shallow: Boolean? = definedExternally /* null */): TreeNode
open fun checked(): Boolean
open fun clean(): TreeNode
open fun clone(excludeKeys: Array<String>? = definedExternally /* null */): TreeNode
open fun collapse(): TreeNode
open fun collapsed(): Boolean
open var text: String
open var id: String
open var itree: Any
open fun context(): TreeNodes
open fun copy(
dest: InspireTree,
hierarchy: Boolean? = definedExternally /* null */,
includeState: Boolean? = definedExternally /* null */
): TreeNode
open fun copyHierarchy(
excludeNode: Boolean? = definedExternally /* null */,
includeState: Boolean? = definedExternally /* null */
): TreeNode
open fun deselect(shallow: Boolean? = definedExternally /* null */): TreeNode
open fun editable(): Boolean
open fun editing(): Boolean
open fun expand(): Promise<TreeNode>
open fun expanded(): Boolean
open fun expandParents(): TreeNode
open fun focus(): TreeNode
open fun focused(): Boolean
open fun getChildren(): TreeNodes
open fun getParent(): TreeNode
open fun getParents(): TreeNodes
open fun getTextualHierarchy(): Array<String>
open fun hasAncestor(): Boolean
open fun hasChildren(): Boolean
open fun hasOrWillHaveChildren(): Boolean
open fun hasParent(): Boolean
open fun hasVisibleChildren(): Boolean
open fun hidden(): Boolean
open fun hide(): TreeNode
open fun indeterminate(): Boolean
open fun indexPath(): String
open fun isFirstRenderable(): Boolean
open fun isLastRenderable(): Boolean
open fun lastDeepestVisibleChild(): TreeNode
open fun loadChildren(): Promise<TreeNodes>
open fun loading(): Boolean
open fun markDirty(): TreeNode
open fun matched(): TreeNodes
open fun nextVisibleAncestralSiblingNode(): TreeNode
open fun nextVisibleChildNode(): TreeNode
open fun nextVisibleNode(): TreeNode
open fun nextVisibleSiblingNode(): TreeNode
open fun pagination(): Pagination
open fun previousVisibleNode(): TreeNode
open fun previousVisibleSiblingNode(): TreeNode
open fun recurseDown(iteratee: NodeIteratee): TreeNode
open fun recurseUp(iteratee: NodeIteratee): TreeNode
open fun refreshIndeterminateState(): TreeNode
open fun reload(): Promise<TreeNodes>
open fun remove(includeState: Boolean? = definedExternally /* null */): Any
open fun removed(): Boolean
open fun renderable(): Boolean
open fun rendered(): Boolean
open fun restore(): TreeNode
open fun select(shallow: Boolean? = definedExternally /* null */): TreeNode
open fun selectable(): Boolean
open fun selected(): Boolean
open fun set(key: Number, `val`: Any): TreeNode
open fun set(key: String, `val`: Any): TreeNode
open fun show(): TreeNode
open fun softRemove(): TreeNode
open fun state(key: Any?, `val`: Boolean? = definedExternally /* null */): dynamic /* Boolean | Any? */
open fun state(key: String, `val`: Boolean? = definedExternally /* null */): dynamic /* Boolean | Any? */
open fun states(keys: Array<String>, `val`: Boolean): Boolean
open fun toggleCheck(): TreeNode
open fun toggleCollapse(): TreeNode
open fun toggleEditing(): TreeNode
open fun toggleSelect(): TreeNode
open fun toObject(
excludeChildren: Boolean? = definedExternally /* null */,
includeState: Boolean? = definedExternally /* null */
): Any
override fun toString(): String
open fun tree(): InspireTree
open fun uncheck(shallow: Boolean? = definedExternally /* null */): TreeNode
open fun visible(): Boolean
}

View File

@ -0,0 +1,110 @@
@file:Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
package hep.dataforge.vis.spatial.tree
import hep.dataforge.meta.string
import hep.dataforge.names.EmptyName
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.vis.common.VisualGroup
import hep.dataforge.vis.common.VisualObject
import hep.dataforge.vis.common.getProperty
import hep.dataforge.vis.spatial.selected
import hep.dataforge.vis.spatial.visible
import org.w3c.dom.HTMLElement
import kotlin.js.json
operator fun Name.plus(other: NameToken): Name = Name(tokens + other)
fun InspireTree.render(element: HTMLElement, block: DomConfig.() -> Unit = {}) {
val config = (json(
"target" to element
) as DomConfig).apply(block)
InspireTreeDOM(this, config)
}
internal fun createInspireTree(block: Config.() -> Unit = {}): InspireTree {
val config = (json(
"checkbox" to json(
"autoCheckChildren" to false
)
) as Config).apply(block)
return InspireTree(config)
}
fun VisualGroup.toTree(onFocus: (VisualObject?, String?)->Unit = {obj,name->}): InspireTree {
val map = HashMap<String, VisualObject>()
fun generateNodeConfig(item: VisualObject, fullName: Name): NodeConfig {
val title = item.getProperty("title").string ?: fullName.last()?.toString() ?: "root"
val text = "$title[${item::class.toString().replace("class ","")}]"
return json(
"children" to if ((item as? VisualGroup)?.children?.isEmpty() != false) {
emptyArray<NodeConfig>()
} else {
true
},
"text" to text,
"id" to fullName.toString(),
"itree" to json(
"state" to json(
"checked" to (item.visible ?: true)
)
)
) as NodeConfig
}
fun TreeNode.fillChildren(group: VisualGroup, groupName: Name) {
group.children.forEach { (token, obj) ->
val name = groupName + token
val nodeConfig = generateNodeConfig(obj, name)
val childNode = addChild(nodeConfig)
map[childNode.id] = obj
if (obj is VisualGroup) {
childNode.fillChildren(obj, name)
}
}
}
val inspireTree = createInspireTree{
}
val nodeConfig = generateNodeConfig(this, EmptyName)
val rootNode = inspireTree.addNode(nodeConfig)
map[rootNode.id] = this
rootNode.fillChildren(this, EmptyName)
inspireTree.on("node.selected") { node: TreeNode, isLoadEvent: Boolean ->
if (!isLoadEvent) {
map[node.id]?.selected = node.selected()
}
}
inspireTree.on("node.deselect") { node: TreeNode ->
map[node.id]?.selected = node.selected()
}
inspireTree.on("node.checked") { node: TreeNode, isLoadEvent: Boolean ->
if (!isLoadEvent) {
map[node.id]?.visible = node.checked()
}
}
inspireTree.on("node.unchecked") { node: TreeNode ->
if(!node.indeterminate()){
map[node.id]?.visible = node.checked()
}
}
inspireTree.on("node.focused") { node: TreeNode, isLoadEvent: Boolean ->
if (!isLoadEvent) {
onFocus(map[node.id],node.id)
}
}
inspireTree.collapseDeep()
return inspireTree
}

View File

@ -60,7 +60,9 @@ class ThreeDemoGrid(meta: Meta) : AbstractPlugin(meta), OutputManager {
gridRoot.append { gridRoot.append {
span("border") { span("border") {
div("col-6") { div("col-6") {
output.attach(div { id = "output-$name" }) { 500 } div { id = "output-$name" }.also{
output.attach(it)
}
hr() hr()
h2 { +(meta["title"].string ?: name.toString()) } h2 { +(meta["title"].string ?: name.toString()) }
} }