forked from kscience/visionforge
UI tune-up
This commit is contained in:
parent
62a6eafdeb
commit
de2ef1dcc5
@ -19,7 +19,7 @@ allprojects {
|
||||
}
|
||||
|
||||
group = "hep.dataforge"
|
||||
version = "0.1.5-dev"
|
||||
version = "0.1.5-dev-2"
|
||||
}
|
||||
|
||||
val githubProject by extra("visionforge")
|
||||
|
@ -7,10 +7,7 @@ import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionGroup
|
||||
import hep.dataforge.vision.bootstrap.*
|
||||
import hep.dataforge.vision.gdml.toVision
|
||||
import hep.dataforge.vision.react.component
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.flexColumn
|
||||
import hep.dataforge.vision.react.state
|
||||
import hep.dataforge.vision.react.*
|
||||
import hep.dataforge.vision.solid.Solid
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
import hep.dataforge.vision.solid.specifications.Camera
|
||||
@ -98,7 +95,7 @@ val GDMLApp = component<GDMLAppProps> { props ->
|
||||
}
|
||||
}
|
||||
//tree
|
||||
card("Object tree", "overflow-auto") {
|
||||
card("Object tree") {
|
||||
visual?.let {
|
||||
objectTree(it, selected, select)
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.bootstrap.card
|
||||
import hep.dataforge.vision.bootstrap.objectTree
|
||||
import hep.dataforge.vision.react.component
|
||||
import hep.dataforge.vision.react.configEditor
|
||||
import hep.dataforge.vision.react.objectTree
|
||||
import hep.dataforge.vision.react.state
|
||||
import hep.dataforge.vision.solid.specifications.Camera
|
||||
import hep.dataforge.vision.solid.specifications.Canvas3DOptions
|
||||
|
@ -2,8 +2,8 @@ import hep.dataforge.context.Global
|
||||
import hep.dataforge.js.Application
|
||||
import hep.dataforge.js.startApplication
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.vision.bootstrap.objectTree
|
||||
import hep.dataforge.vision.bootstrap.visualPropertyEditor
|
||||
import hep.dataforge.vision.react.objectTree
|
||||
import hep.dataforge.vision.solid.Point3D
|
||||
import hep.dataforge.vision.solid.SolidGroup
|
||||
import hep.dataforge.vision.solid.box
|
||||
|
@ -7,11 +7,8 @@ import hep.dataforge.names.NameToken
|
||||
import kotlinx.html.classes
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.events.Event
|
||||
import react.RBuilder
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
import react.*
|
||||
import react.dom.*
|
||||
import react.setState
|
||||
|
||||
interface MetaViewerProps : RProps {
|
||||
var name: NameToken
|
||||
@ -19,6 +16,12 @@ interface MetaViewerProps : RProps {
|
||||
var descriptor: NodeDescriptor?
|
||||
}
|
||||
|
||||
|
||||
interface TreeState : RState {
|
||||
var expanded: Boolean
|
||||
}
|
||||
|
||||
@Deprecated("To be replaced by react functional component")
|
||||
class MetaViewerComponent : RComponent<MetaViewerProps, TreeState>() {
|
||||
|
||||
override fun TreeState.init() {
|
||||
|
@ -2,12 +2,16 @@ package hep.dataforge.vision.bootstrap
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.react.ObjectTree
|
||||
import kotlinx.html.*
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import react.RBuilder
|
||||
import react.ReactElement
|
||||
import react.child
|
||||
import react.dom.*
|
||||
|
||||
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
|
||||
@ -206,3 +210,19 @@ inline fun RBuilder.gridRow(
|
||||
): ReactElement {
|
||||
return div(joinStyles(classes, "row"), block)
|
||||
}
|
||||
|
||||
fun Element.renderObjectTree(
|
||||
vision: Vision,
|
||||
clickCallback: (Name) -> Unit = {}
|
||||
) = render(this) {
|
||||
card("Object tree") {
|
||||
child(ObjectTree) {
|
||||
attrs {
|
||||
this.name = Name.EMPTY
|
||||
this.obj = vision
|
||||
this.selected = null
|
||||
this.clickCallback = clickCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,13 +6,10 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.values.Value
|
||||
import kotlinx.css.*
|
||||
import kotlinx.css.properties.TextDecoration
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.div
|
||||
import react.dom.render
|
||||
import styled.*
|
||||
|
||||
@ -86,7 +83,10 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
|
||||
when (actualItem) {
|
||||
is MetaItem.NodeItem -> {
|
||||
div {
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeLeaf
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeCaret
|
||||
@ -145,15 +145,14 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeLeaf
|
||||
justifyContent = JustifyContent.flexEnd
|
||||
// justifyContent = JustifyContent.flexEnd
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
flexGrow = 1.0
|
||||
+TreeStyles.treeLabel
|
||||
}
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeLabel
|
||||
if (item == null) {
|
||||
+TreeStyles.treeLabelInactive
|
||||
}
|
||||
@ -162,6 +161,9 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
}
|
||||
}
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.resizeableInput
|
||||
}
|
||||
valueChooser(
|
||||
props.name,
|
||||
actualItem,
|
||||
@ -169,37 +171,20 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
|
||||
valueChanged
|
||||
)
|
||||
}
|
||||
styledDiv {
|
||||
styledButton {
|
||||
css {
|
||||
flexShrink = 1.0
|
||||
+TreeStyles.removeButton
|
||||
}
|
||||
styledButton {
|
||||
css {
|
||||
backgroundColor = Color.white
|
||||
borderStyle = BorderStyle.solid
|
||||
borderRadius = 2.px
|
||||
padding(1.px, 5.px)
|
||||
marginLeft = 4.px
|
||||
textAlign = TextAlign.center
|
||||
textDecoration = TextDecoration.none
|
||||
display = Display.inlineBlock
|
||||
cursor = Cursor.pointer
|
||||
disabled {
|
||||
cursor = Cursor.auto
|
||||
borderStyle = BorderStyle.dashed
|
||||
color = Color.lightGray
|
||||
}
|
||||
}
|
||||
+"\u00D7"
|
||||
attrs {
|
||||
if (item == null) {
|
||||
disabled = true
|
||||
} else {
|
||||
onClickFunction = removeClick
|
||||
}
|
||||
+"\u00D7"
|
||||
attrs {
|
||||
if (item == null) {
|
||||
disabled = true
|
||||
} else {
|
||||
onClickFunction = removeClick
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package hep.dataforge.vision.bootstrap
|
||||
package hep.dataforge.vision.react
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.plus
|
||||
@ -6,14 +6,10 @@ import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.vision.Vision
|
||||
import hep.dataforge.vision.VisionGroup
|
||||
import hep.dataforge.vision.isEmpty
|
||||
import hep.dataforge.vision.react.RFBuilder
|
||||
import hep.dataforge.vision.react.component
|
||||
import kotlinx.html.classes
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.*
|
||||
import styled.*
|
||||
|
||||
interface ObjectTreeProps : RProps {
|
||||
var name: Name
|
||||
@ -22,24 +18,25 @@ interface ObjectTreeProps : RProps {
|
||||
var clickCallback: (Name) -> Unit
|
||||
}
|
||||
|
||||
interface TreeState : RState {
|
||||
var expanded: Boolean
|
||||
}
|
||||
|
||||
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
var expanded: Boolean by useState{ props.selected?.startsWith(props.name) ?: false }
|
||||
var expanded: Boolean by useState { props.selected?.startsWith(props.name) ?: false }
|
||||
|
||||
val onClick: (Event) -> Unit = {
|
||||
expanded = !expanded
|
||||
}
|
||||
|
||||
fun RBuilder.treeLabel(text: String) {
|
||||
button(classes = "btn btn-link align-middle tree-label p-0") {
|
||||
styledButton {
|
||||
css {
|
||||
//classes = mutableListOf("btn", "btn-link", "align-middle", "text-truncate", "p-0")
|
||||
+TreeStyles.treeLabel
|
||||
+TreeStyles.linkButton
|
||||
if (props.name == props.selected) {
|
||||
+TreeStyles.treeLabelSelected
|
||||
}
|
||||
}
|
||||
+text
|
||||
attrs {
|
||||
if (props.name == props.selected) {
|
||||
classes += "tree-label-selected"
|
||||
}
|
||||
onClickFunction = { props.clickCallback(props.name) }
|
||||
}
|
||||
}
|
||||
@ -50,13 +47,19 @@ private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
|
||||
//display as node if any child is visible
|
||||
if (obj is VisionGroup) {
|
||||
div("d-block text-truncate") {
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeLeaf
|
||||
}
|
||||
if (obj.children.any { !it.key.body.startsWith("@") }) {
|
||||
span("tree-caret") {
|
||||
attrs {
|
||||
styledSpan {
|
||||
css {
|
||||
+TreeStyles.treeCaret
|
||||
if (expanded) {
|
||||
classes += "tree-caret-down"
|
||||
+TreeStyles.treeCaredDown
|
||||
}
|
||||
}
|
||||
attrs {
|
||||
onClickFunction = onClick
|
||||
}
|
||||
}
|
||||
@ -64,12 +67,18 @@ private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
treeLabel(token)
|
||||
}
|
||||
if (expanded) {
|
||||
ul("tree") {
|
||||
styledUl {
|
||||
css {
|
||||
+TreeStyles.tree
|
||||
}
|
||||
obj.children.entries
|
||||
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true }
|
||||
.sortedBy { (it.value as? VisionGroup)?.isEmpty ?: true } // ignore empty groups
|
||||
.forEach { (childToken, child) ->
|
||||
li("tree-item") {
|
||||
styledLi {
|
||||
css {
|
||||
+TreeStyles.treeItem
|
||||
}
|
||||
child(ObjectTree) {
|
||||
attrs {
|
||||
this.name = props.name + childToken
|
||||
@ -83,8 +92,10 @@ private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
div("d-block text-truncate") {
|
||||
span("tree-leaf") {}
|
||||
styledDiv {
|
||||
css {
|
||||
+TreeStyles.treeLeaf
|
||||
}
|
||||
treeLabel(token)
|
||||
}
|
||||
}
|
||||
@ -94,22 +105,6 @@ val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
||||
objectTree(props)
|
||||
}
|
||||
|
||||
fun Element.renderObjectTree(
|
||||
vision: Vision,
|
||||
clickCallback: (Name) -> Unit = {}
|
||||
) = render(this) {
|
||||
card("Object tree") {
|
||||
child(ObjectTree) {
|
||||
attrs {
|
||||
this.name = Name.EMPTY
|
||||
this.obj = vision
|
||||
this.selected = null
|
||||
this.clickCallback = clickCallback
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RBuilder.objectTree(
|
||||
vision: Vision,
|
||||
selected: Name? = null,
|
@ -1,8 +1,7 @@
|
||||
package hep.dataforge.vision.react
|
||||
|
||||
import kotlinx.css.*
|
||||
import kotlinx.css.properties.deg
|
||||
import kotlinx.css.properties.rotate
|
||||
import kotlinx.css.properties.*
|
||||
import styled.StyleSheet
|
||||
|
||||
object TreeStyles : StyleSheet("treeStyles", true) {
|
||||
@ -30,22 +29,6 @@ object TreeStyles : StyleSheet("treeStyles", true) {
|
||||
}
|
||||
}
|
||||
|
||||
val treeItem by css {
|
||||
alignItems = Align.center
|
||||
paddingLeft = 10.px
|
||||
borderLeftStyle = BorderStyle.dashed
|
||||
borderLeftWidth = 1.px
|
||||
borderLeftColor = Color.lightGray
|
||||
}
|
||||
|
||||
val treeLeaf by css {
|
||||
display = Display.flex
|
||||
flexDirection = FlexDirection.row
|
||||
userSelect = UserSelect.none
|
||||
alignItems = Align.center
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rotate the caret/arrow icon when clicked on (using JavaScript)
|
||||
*/
|
||||
@ -59,8 +42,27 @@ object TreeStyles : StyleSheet("treeStyles", true) {
|
||||
}
|
||||
}
|
||||
|
||||
val treeItem by css {
|
||||
alignItems = Align.center
|
||||
paddingLeft = 10.px
|
||||
borderLeftStyle = BorderStyle.dashed
|
||||
borderLeftWidth = 1.px
|
||||
borderLeftColor = Color.lightGray
|
||||
borderBottomStyle = BorderStyle.dashed
|
||||
borderBottomWidth = 1.px
|
||||
borderBottomColor = Color.lightGray
|
||||
}
|
||||
|
||||
val treeLeaf by css {
|
||||
display = Display.flex
|
||||
flexDirection = FlexDirection.row
|
||||
flexWrap = FlexWrap.nowrap
|
||||
//alignItems = Align.center
|
||||
}
|
||||
|
||||
val treeLabel by css {
|
||||
overflow = Overflow.hidden
|
||||
flex(flexGrow = 1.0, flexShrink = 1.0)
|
||||
}
|
||||
|
||||
val treeLabelInactive by css {
|
||||
@ -71,4 +73,49 @@ object TreeStyles : StyleSheet("treeStyles", true) {
|
||||
backgroundColor = Color.lightBlue
|
||||
}
|
||||
|
||||
val linkButton by css {
|
||||
backgroundColor = Color.white
|
||||
border = "none"
|
||||
padding(left = 4.pt, right = 4.pt, top = 0.pt, bottom = 0.pt)
|
||||
textAlign = TextAlign.left
|
||||
fontFamily = "arial,sans-serif"
|
||||
color = Color("#069")
|
||||
cursor = Cursor.pointer
|
||||
hover {
|
||||
textDecoration(TextDecorationLine.underline)
|
||||
}
|
||||
}
|
||||
|
||||
val removeButton by css {
|
||||
backgroundColor = Color.white
|
||||
borderStyle = BorderStyle.solid
|
||||
borderRadius = 2.px
|
||||
padding(1.px, 5.px)
|
||||
marginLeft = 4.px
|
||||
textAlign = TextAlign.center
|
||||
textDecoration = TextDecoration.none
|
||||
display = Display.inlineBlock
|
||||
flexShrink = 1.0
|
||||
cursor = Cursor.pointer
|
||||
disabled {
|
||||
cursor = Cursor.auto
|
||||
borderStyle = BorderStyle.dashed
|
||||
color = Color.lightGray
|
||||
}
|
||||
}
|
||||
|
||||
val resizeableInput by css {
|
||||
overflow = Overflow.hidden
|
||||
maxWidth = 120.pt
|
||||
flex(flexGrow = 2.0, flexShrink = 2.0, flexBasis = 60.pt)
|
||||
input {
|
||||
textAlign = TextAlign.right
|
||||
width = 100.pct
|
||||
}
|
||||
select{
|
||||
textAlign = TextAlign.right
|
||||
width = 100.pct
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,8 +6,6 @@ import hep.dataforge.names.Name
|
||||
import hep.dataforge.values.*
|
||||
import hep.dataforge.vision.Colors
|
||||
import hep.dataforge.vision.widgetType
|
||||
import kotlinx.css.Align
|
||||
import kotlinx.css.alignSelf
|
||||
import kotlinx.html.InputType
|
||||
import kotlinx.html.js.onChangeFunction
|
||||
import kotlinx.html.js.onKeyDownFunction
|
||||
@ -17,11 +15,9 @@ import org.w3c.dom.HTMLSelectElement
|
||||
import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.defaultValue
|
||||
import react.dom.input
|
||||
import react.dom.option
|
||||
import react.dom.select
|
||||
import styled.css
|
||||
import styled.styledDiv
|
||||
import styled.styledInput
|
||||
import styled.styledSelect
|
||||
|
||||
interface ValueChooserProps : RProps {
|
||||
var item: MetaItem<*>?
|
||||
@ -75,7 +71,7 @@ class ValueChooserComponent(props: ValueChooserProps) : RComponent<ValueChooserP
|
||||
}
|
||||
}
|
||||
|
||||
private fun RBuilder.stringInput() = input(type = InputType.text) {
|
||||
private fun RBuilder.stringInput() = styledInput(type = InputType.text) {
|
||||
attrs {
|
||||
this.defaultValue = props.item?.string ?: ""
|
||||
onKeyDownFunction = keyDown
|
||||
@ -84,67 +80,61 @@ class ValueChooserComponent(props: ValueChooserProps) : RComponent<ValueChooserP
|
||||
}
|
||||
|
||||
override fun RBuilder.render() {
|
||||
styledDiv {
|
||||
css {
|
||||
alignSelf = Align.center
|
||||
val descriptor = props.descriptor
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
when {
|
||||
state.rawInput == true -> stringInput()
|
||||
descriptor?.widgetType == "color" -> styledInput(type = InputType.color) {
|
||||
ref = element
|
||||
attrs {
|
||||
this.defaultValue = props.item?.value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else value.string
|
||||
} ?: "#000000"
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
val descriptor = props.descriptor
|
||||
val type = descriptor?.type?.firstOrNull()
|
||||
when {
|
||||
state.rawInput == true -> stringInput()
|
||||
descriptor?.widgetType == "color" -> input(type = InputType.color) {
|
||||
type == ValueType.BOOLEAN -> {
|
||||
styledInput(type = InputType.checkBox) {
|
||||
ref = element
|
||||
attrs {
|
||||
this.defaultValue = props.item?.value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
else value.string
|
||||
} ?: "#000000"
|
||||
defaultChecked = props.item?.boolean ?: false
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
type == ValueType.BOOLEAN -> {
|
||||
input(type = InputType.checkBox) {
|
||||
ref = element
|
||||
attrs {
|
||||
defaultChecked = props.item?.boolean ?: false
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
}
|
||||
type == ValueType.NUMBER -> input(type = InputType.number) {
|
||||
ref = element
|
||||
attrs {
|
||||
descriptor.attributes["step"].string?.let {
|
||||
step = it
|
||||
}
|
||||
descriptor.attributes["min"].string?.let {
|
||||
min = it
|
||||
}
|
||||
descriptor.attributes["max"].string?.let {
|
||||
max = it
|
||||
}
|
||||
defaultValue = props.item?.string ?: ""
|
||||
onKeyDownFunction = keyDown
|
||||
}
|
||||
}
|
||||
descriptor?.allowedValues?.isNotEmpty() ?: false -> select {
|
||||
descriptor!!.allowedValues.forEach {
|
||||
option {
|
||||
+it.string
|
||||
}
|
||||
}
|
||||
ref = element
|
||||
attrs {
|
||||
this.value = props.item?.string ?: ""
|
||||
multiple = false
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
else -> stringInput()
|
||||
}
|
||||
type == ValueType.NUMBER -> styledInput(type = InputType.number) {
|
||||
ref = element
|
||||
attrs {
|
||||
descriptor.attributes["step"].string?.let {
|
||||
step = it
|
||||
}
|
||||
descriptor.attributes["min"].string?.let {
|
||||
min = it
|
||||
}
|
||||
descriptor.attributes["max"].string?.let {
|
||||
max = it
|
||||
}
|
||||
defaultValue = props.item?.string ?: ""
|
||||
onKeyDownFunction = keyDown
|
||||
}
|
||||
}
|
||||
descriptor?.allowedValues?.isNotEmpty() ?: false -> styledSelect {
|
||||
descriptor!!.allowedValues.forEach {
|
||||
option {
|
||||
+it.string
|
||||
}
|
||||
}
|
||||
ref = element
|
||||
attrs {
|
||||
this.value = props.item?.string ?: ""
|
||||
multiple = false
|
||||
onChangeFunction = commit
|
||||
}
|
||||
}
|
||||
else -> stringInput()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun RBuilder.valueChooser(
|
||||
|
Loading…
Reference in New Issue
Block a user