Fix design issues with labels/breadcrumbs

This commit is contained in:
Alexander Nozik 2020-04-18 16:06:00 +03:00
parent ea73650b50
commit ac86832616
12 changed files with 155 additions and 132 deletions

View File

@ -86,6 +86,10 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
}
}
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()
@ -98,6 +102,15 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
}
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
}
@ -195,9 +208,22 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
}
}
}
if (item != null) {
div("col-auto") {
button(classes = "btn btn-default") {
span("glyphicon glyphicon-remove") {
attrs {
attributes["aria-hidden"] = "true"
}
}
attrs {
onClickFunction = removeValue
}
}
}
}
div("col") {
valueChooser(actualItem.value, descriptorItem as? ValueDescriptor)
//+actualItem.value.toString()
}
}
}
@ -220,6 +246,7 @@ fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, def
}
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) {
div {
child(ConfigEditorComponent::class) {
attrs {
root = config
@ -228,15 +255,9 @@ fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, de
this.default = default
}
}
}
}
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
child(ConfigEditorComponent::class) {
attrs {
root = obj.config
name = Name.EMPTY
this.descriptor = descriptor
this.default = default
}
}
configEditor(obj.config, descriptor ?: obj.descriptor, default)
}

View File

@ -39,7 +39,7 @@ class ObjectTree : RComponent<ObjectTreeProps, TreeState>() {
}
private fun RBuilder.treeLabel(text: String) {
a("#", classes = "tree-label") {
button(classes = "btn btn-link tree-label p-0") {
+text
attrs {
if (props.name == props.selected) {

View File

@ -2,7 +2,6 @@ package hep.dataforge.vis.editor
import hep.dataforge.js.card
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
@ -19,10 +18,9 @@ fun RBuilder.visualPropertyEditor(
path: Name,
item: VisualObject,
descriptor: NodeDescriptor? = item.descriptor,
title: String = "Properties",
default: MetaBuilder.() -> Unit = {}
default: Meta? = null
) {
card(title) {
card("Properties") {
if (!path.isEmpty()) {
nav {
attrs {
@ -37,7 +35,7 @@ fun RBuilder.visualPropertyEditor(
}
}
}
configEditor(item, descriptor, Meta(default))
configEditor(item, descriptor, default)
}
}
@ -45,8 +43,7 @@ fun Element.visualPropertyEditor(
path: Name,
item: VisualObject,
descriptor: NodeDescriptor? = item.descriptor,
title: String = "Properties",
default: MetaBuilder.() -> Unit = {}
default: Meta? = null
) = render(this) {
visualPropertyEditor(path, item, descriptor, title, default)
this.visualPropertyEditor(path, item, descriptor, default)
}

View File

@ -0,0 +1,11 @@
package hep.dataforge.vis.editor
//val TextValueChooser = functionalComponent<ConfigEditorProps> {
//
// input(type = InputType.number, classes = "float-right") {
// attrs {
// defaultValue = value.string
// onChangeFunction = onValueChange
// }
// }
//}

View File

@ -42,3 +42,7 @@ ul, .tree {
.tree-label-selected{
background-color: lightblue;
}
.no-padding{
padding: 0;
}

View File

@ -56,6 +56,13 @@ class Material3D : Scheme() {
defineValue(OPACITY_KEY) {
type(ValueType.NUMBER)
default(1.0)
configure {
"attributes" to {
this["min"] = 0.0
this["max"] = 1.0
this["step"] = 0.1
}
}
}
defineValue(WIREFRAME_KEY) {
type(ValueType.BOOLEAN)

View File

@ -168,11 +168,16 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
}
}
override fun render(obj: VisualObject3D, meta: Meta) {
//clear old root
fun clear(){
scene.children.find { it.name == "@root" }?.let {
scene.remove(it)
}
}
override fun render(obj: VisualObject3D, meta: Meta) {
//clear old root
clear()
val object3D = three.buildObject3D(obj)
object3D.name = "@root"

View File

@ -12,7 +12,6 @@ import react.RProps
import react.RState
import react.dom.div
import react.dom.findDOMNode
import kotlin.dom.clear
interface ThreeCanvasProps : RProps {
var context: Context
@ -33,18 +32,21 @@ class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
var canvas: ThreeCanvas? = null
override fun componentDidMount() {
if(canvas == null) {
val element = state.element as? HTMLElement ?: error("Canvas element not found")
val three: ThreePlugin = props.context.plugins.load(ThreePlugin)
canvas = three.output(element, props.options ?: Canvas.empty())
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
canvas = three.output(element, props.options ?: Canvas.empty()).apply {
onClick = props.clickCallback
}
props.canvasCallback?.invoke(canvas)
}
canvas?.render(props.obj)
canvas?.onClick = props.clickCallback
}
override fun componentWillUnmount() {
state.element?.clear()
props.canvasCallback?.invoke(null)
}
// override fun componentWillUnmount() {
// state.element?.clear()
// props.canvasCallback?.invoke(null)
// }
override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
if (prevProps.obj != props.obj) {

View File

@ -159,6 +159,7 @@ private class GDMLDemoApp : Application {
}
canvas.select(name)
editorElement.visualPropertyEditor(name, child)
}
// canvas.clickListener = ::selectElement

View File

@ -20,6 +20,9 @@ kotlin {
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
}
}
webpackTask {
mode = org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode.PRODUCTION
}
}
}

View File

@ -3,10 +3,11 @@ package ru.mipt.npm.muon.monitor
import hep.dataforge.context.Context
import hep.dataforge.js.card
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.editor.configEditor
import hep.dataforge.vis.editor.objectTree
import hep.dataforge.vis.editor.visualPropertyEditor
import hep.dataforge.vis.spatial.Visual3D
import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.specifications.Camera
import hep.dataforge.vis.spatial.specifications.Canvas
@ -14,43 +15,27 @@ import hep.dataforge.vis.spatial.three.ThreeCanvas
import hep.dataforge.vis.spatial.three.ThreeCanvasComponent
import hep.dataforge.vis.spatial.three.canvasControls
import io.ktor.client.HttpClient
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.request.get
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.html.js.onClickFunction
import kotlinx.serialization.json.Json
import react.*
import react.dom.button
import react.dom.div
import react.dom.*
import kotlin.math.PI
interface MMAppProps : RProps {
var model: Model
var context: Context
var connection: HttpClient
}
interface MMAppState : RState {
var model: Model
var selected: Name?
var canvas: ThreeCanvas?
}
class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
private val model = Model()
private val connection = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer(Json(context = Visual3D.serialModule))
}
}
override fun MMAppState.init(props: MMAppProps) {
this.model = props.model
}
private val onSelect: (Name?) -> Unit = {
setState {
selected = it
@ -66,7 +51,7 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
}
override fun RBuilder.render() {
val visual = model.root
val visual = props.model.root
val selected = state.selected
div("row") {
@ -86,7 +71,7 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
this.selected = selected
this.clickCallback = onSelect
this.canvasCallback = {
setState{
setState {
canvas = it
}
}
@ -96,15 +81,19 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
div("col-lg-3") {
div("row") {
//settings
state.canvas?.let { canvasControls(it) }
state.canvas?.let {
card("Canvas configuration") {
canvasControls(it)
}
}
card("Events") {
button {
+"Next"
attrs {
onClickFunction = {
GlobalScope.launch {
val event = connection.get<Event>("http://localhost:8080/event")
model.displayEvent(event)
val event = props.connection.get<Event>("http://localhost:8080/event")
props.model.displayEvent(event)
}
}
}
@ -113,7 +102,50 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
+"Clear"
attrs {
onClickFunction = {
model.reset()
props.model.reset()
}
}
}
}
}
div("row") {
div("container-fluid p-0") {
nav {
attrs {
attributes["aria-label"] = "breadcrumb"
}
ol("breadcrumb") {
li("breadcrumb-item") {
button(classes = "btn btn-link p-0") {
+"World"
attrs {
onClickFunction = {
setState {
this.selected = Name.EMPTY
}
}
}
}
}
if (selected != null) {
val tokens = ArrayList<NameToken>(selected.length)
selected.tokens.forEach { token ->
tokens.add(token)
val fullName = Name(tokens.toList())
li("breadcrumb-item") {
button(classes = "btn btn-link p-0") {
+token.toString()
attrs {
onClickFunction = {
setState {
console.log("Selected = $fullName")
this.selected = fullName
}
}
}
}
}
}
}
}
}
@ -122,13 +154,14 @@ class MMAppComponent : RComponent<MMAppProps, MMAppState>() {
div("row") {
//properties
if (selected != null) {
val selectedObject = when {
val selectedObject: VisualObject? = when {
selected.isEmpty() -> visual
else -> visual[selected]
}
if (selectedObject != null) {
//TODO replace by explicit breadcrumbs with callback
visualPropertyEditor(selected, selectedObject, descriptor = VisualObject3D.descriptor)
card("Properties") {
configEditor(selectedObject, descriptor = VisualObject3D.descriptor)
}
}
}
}

View File

@ -21,83 +21,22 @@ private class MMDemoApp : Application {
}
}
//TODO introduce react application
override fun start(state: Map<String, Any>) {
val context = Global.context("demo") {}
// val three = context.plugins.load(ThreePlugin)
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
render(element) {
child(MMAppComponent::class) {
attrs {
this.model = model
model = this@MMDemoApp.model
connection = this@MMDemoApp.connection
this.context = context
}
}
}
// //val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
//
// val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
// val settingsElement = document.getElementById("settings")
// ?: error("Element with id 'settings' not found on page")
// val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
// val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
//
// canvasElement.clear()
// val visual = model.root
//
// //output.camera.layers.enable(1)
// val canvas = three.output(canvasElement as HTMLElement)
//
// canvas.camera.layers.set(0)
// canvas.camera.position.z = -2000.0
// canvas.camera.position.y = 500.0
// canvas.camera.lookAt(Vector3(0, 0, 0))
//
// settingsElement.displayCanvasControls(canvas) {
// card("Events") {
// button {
// +"Next"
// onClickFunction = {
// GlobalScope.launch {
// val event = connection.get<Event>("http://localhost:8080/event")
// model.displayEvent(event)
// }
// }
// }
// button {
// +"Clear"
// onClickFunction = {
// model.reset()
// }
// }
// }
// }
//
// var objectTreeContainer: ObjectTreeContainer? = null
//
// fun selectElement(name: Name?) {
// if (name != null) {
// canvas.select(name)
// val child: VisualObject = when {
// name.isEmpty() -> visual
// visual is VisualGroup -> visual[name] ?: return
// else -> return
// }
// editorElement.visualPropertyEditor(name, child, descriptor = VisualObject3D.descriptor)
// objectTreeContainer?.select(name)
// }
// }
//
// val selectElementFunction: (Name?) -> Unit = { name ->
// selectElement(name?.selectable())
// }
//
// canvas.onClick = selectElementFunction
//
// objectTreeContainer = treeElement.renderObjectTree(visual, selectElementFunction)
// canvas.render(visual)
}
}