UI tune-up

This commit is contained in:
Alexander Nozik 2020-08-09 14:41:24 +03:00
parent 62a6eafdeb
commit de2ef1dcc5
10 changed files with 199 additions and 162 deletions

View File

@ -19,7 +19,7 @@ allprojects {
}
group = "hep.dataforge"
version = "0.1.5-dev"
version = "0.1.5-dev-2"
}
val githubProject by extra("visionforge")

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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) {
@ -205,4 +209,20 @@ inline fun RBuilder.gridRow(
block: RDOMBuilder<DIV>.() -> Unit
): 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
}
}
}
}

View File

@ -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
}
}
}
}
}
}

View File

@ -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,

View File

@ -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
}
}
}

View File

@ -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(