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.names.Name
import hep.dataforge.names.toName
import hep.dataforge.provider.Type
import hep.dataforge.vis.common.VisualObject.Companion.TYPE
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 {
dependencies {
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
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.startApplication
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.ThreePlugin
import hep.dataforge.vis.spatial.three.output
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import hep.dataforge.vis.spatial.tree.render
import hep.dataforge.vis.spatial.tree.toTree
import kotlinx.html.InputType
import kotlinx.html.dom.append
import kotlinx.html.js.input
import kotlinx.html.js.li
import kotlinx.html.js.ul
import kotlinx.html.js.*
import org.w3c.dom.Element
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement
import org.w3c.dom.events.Event
import org.w3c.files.FileList
import org.w3c.files.FileReader
@ -42,7 +44,7 @@ private class GDMLDemoApp : ApplicationBase() {
/**
* 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.preventDefault()
@ -53,34 +55,46 @@ private class GDMLDemoApp : ApplicationBase() {
FileReader().apply {
onload = {
val string = result as String
GlobalScope.launch {
try {
block(file.name, string)
} catch (ex: Exception) {
console.error(ex)
}
}
readAsText(file)
}
}
private fun spinner(show: Boolean) {
val style = if (show) {
"display:block;"
} else {
"display:none;"
}
document.getElementById("loader")?.setAttribute("style", style)
// if( show){
//
// val style = if (show) {
// "display:block;"
// } else {
// "display:none;"
// }
// document.getElementById("canvas")?.append {
//
// }
}
private fun message(message: String?) {
val element = document.getElementById("message")
document.getElementById("messages")?.let { element ->
if (message == null) {
element?.setAttribute("style", "display:none;")
element.clear()
} else {
element?.textContent = message
element?.setAttribute("style", "display:block;")
element.append {
p {
+message
}
}
}
}
}
fun setupSidebar(element: Element, output: ThreeOutput, root: VisualObject3D) {
fun setupLayers(element: Element, output: ThreeOutput) {
element.clear()
element.append {
ul("list-group") {
@ -103,7 +117,6 @@ private class GDMLDemoApp : ApplicationBase() {
}
}
}
element.appendFancyTree(root)
}
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>) {
val context = Global.context("demo") {}
@ -144,15 +194,16 @@ private class GDMLDemoApp : ApplicationBase() {
//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 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()
val action: suspend CoroutineScope.(name: String, data: String) -> Unit = { name, data ->
val action: (name: String, data: String) -> Unit = { name, data ->
canvas.clear()
launch { spinner(true) }
launch { message("Loading GDML") }
spinner(true)
message("Loading GDML")
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 {
name.endsWith(".gdml") || name.endsWith(".xml") -> gdml.toVisual(gdmlConfiguration)
@ -165,20 +216,23 @@ private class GDMLDemoApp : ApplicationBase() {
}
}
launch { message("Rendering") }
val output = three.output(canvas)
message("Rendering")
val output = three.output(canvas as HTMLElement)
//output.camera.layers.enable(1)
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)
launch {
message(null)
spinner(false)
}
}
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
addEventListener("dragover", { handleDragOver(it) }, false)
@ -187,6 +241,9 @@ private class GDMLDemoApp : ApplicationBase() {
}
override fun dispose(): Map<String, Any> {
return super.dispose()
}
}
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 name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
<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"
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="https://cdn.jsdelivr.net/npm/jquery.fancytree@2.32.0/dist/skin-bootstrap/ui.fancytree.min.css">
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/inspire-tree-light.min.css">
<script type="text/javascript" src="main.bundle.js"></script>
</head>
<body class="testApp">
@ -21,18 +23,53 @@
<div class="container">
<h1>GDML demo</h1>
</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 loader" id="loader" style="display:none;"></div>-->
<div class="container animate-bottom" id="messages"></div>
<div class="container-fluid">
<div class="row">
<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>
<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"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>

View File

@ -5,12 +5,12 @@ plugins {
id("org.openjfx.javafxplugin")
}
scientifik{
scientifik {
withSerialization()
}
kotlin {
jvm{
jvm {
withJava()
}
sourceSets {
@ -19,20 +19,20 @@ kotlin {
api(project(":dataforge-vis-common"))
}
}
jvmMain{
jvmMain {
dependencies {
implementation(project(":dataforge-vis-fx"))
implementation("org.fxyz3d:fxyz3d:0.4.0")
}
}
jsMain{
jsMain {
dependencies {
implementation(npm("three", "0.106.2"))
implementation(npm("@hi-level/three-csg", "1.0.6"))
implementation(npm("style-loader"))
implementation(npm("element-resize-event"))
// api("kotlin.js.externals:kotlin-js-jquery:3.2.0-0")
// implementation(npm("jquery.fancytree","2.32.0"))
implementation(npm("inspire-tree","6.0.1"))
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.LAYER_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 kotlinx.serialization.UseSerializers
@ -34,6 +35,7 @@ interface VisualObject3D : VisualObject {
companion object {
val MATERIAL_KEY = "material".asName()
val VISIBLE_KEY = "visible".asName()
val SELECTED_KEY = "selected".asName()
val DETAIL_KEY = "detail".asName()
val LAYER_KEY = "layer".asName()
@ -117,10 +119,18 @@ var VisualObject.visible: Boolean?
get() = getProperty(VISIBLE_KEY).boolean
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) {
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) {
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.lights.AmbientLight
import info.laht.threekt.scenes.Scene
import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import kotlin.browser.window
import kotlin.dom.clear
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 {
add(AmbientLight())
if(meta["axes.visible"].boolean == true){
if (meta["axes.visible"].boolean == true) {
axes.visible = true
}
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)
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 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())
}
element.replaceWith(renderer.domElement)
element.appendChild(renderer.domElement)
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 {
if (element != null) {
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 {
span("border") {
div("col-6") {
output.attach(div { id = "output-$name" }) { 500 }
div { id = "output-$name" }.also{
output.attach(it)
}
hr()
h2 { +(meta["title"].string ?: name.toString()) }
}