working muon monitor
This commit is contained in:
parent
3f144a5dbd
commit
b5a1296070
@ -17,7 +17,7 @@ public fun NameCrumbs(name: Name?, link: (Name) -> Unit): Unit = Nav({
|
||||
classes("breadcrumb")
|
||||
style {
|
||||
property("--bs-breadcrumb-divider", "'.'")
|
||||
property("--bs-breadcrumb-item-padding-x",".2rem")
|
||||
property("--bs-breadcrumb-item-padding-x",".1rem")
|
||||
}
|
||||
}) {
|
||||
Li({
|
||||
|
@ -1,16 +1,16 @@
|
||||
package space.kscience.visionforge.compose
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import app.softwork.bootstrapcompose.CloseButton
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.web.attributes.disabled
|
||||
import org.jetbrains.compose.web.css.AlignItems
|
||||
import org.jetbrains.compose.web.css.alignItems
|
||||
import org.jetbrains.compose.web.css.px
|
||||
import org.jetbrains.compose.web.css.width
|
||||
import org.jetbrains.compose.web.dom.Button
|
||||
import org.jetbrains.compose.web.dom.Div
|
||||
import org.jetbrains.compose.web.dom.Span
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
@ -35,13 +35,13 @@ public sealed class EditorPropertyState {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param meta Root config object - always non-null
|
||||
* @param rootDescriptor Full path to the displayed node in [meta]. Could be empty
|
||||
* @param rootMeta Root config object - always non-null
|
||||
* @param rootDescriptor Full path to the displayed node in [rootMeta]. Could be empty
|
||||
*/
|
||||
@Composable
|
||||
public fun PropertyEditor(
|
||||
scope: CoroutineScope,
|
||||
meta: MutableMeta,
|
||||
rootMeta: MutableMeta,
|
||||
getPropertyState: (Name) -> EditorPropertyState,
|
||||
updates: Flow<Name>,
|
||||
name: Name = Name.EMPTY,
|
||||
@ -50,11 +50,11 @@ public fun PropertyEditor(
|
||||
) {
|
||||
var expanded: Boolean by remember { mutableStateOf(initialExpanded ?: true) }
|
||||
val descriptor: MetaDescriptor? = remember(rootDescriptor, name) { rootDescriptor?.get(name) }
|
||||
var property: MutableMeta by remember { mutableStateOf(meta.getOrCreate(name)) }
|
||||
var property: MutableMeta by remember { mutableStateOf(rootMeta.getOrCreate(name)) }
|
||||
var editorPropertyState: EditorPropertyState by remember { mutableStateOf(getPropertyState(name)) }
|
||||
|
||||
|
||||
val keys = remember(descriptor) {
|
||||
val keys by derivedStateOf {
|
||||
buildSet {
|
||||
descriptor?.children?.filterNot {
|
||||
it.key.startsWith("@") || it.value.hidden
|
||||
@ -68,11 +68,11 @@ public fun PropertyEditor(
|
||||
val token = name.lastOrNull()?.toString() ?: "Properties"
|
||||
|
||||
fun update() {
|
||||
property = meta.getOrCreate(name)
|
||||
property = rootMeta.getOrCreate(name)
|
||||
editorPropertyState = getPropertyState(name)
|
||||
}
|
||||
|
||||
LaunchedEffect(meta) {
|
||||
LaunchedEffect(rootMeta) {
|
||||
updates.collect { updatedName ->
|
||||
if (updatedName == name) {
|
||||
update()
|
||||
@ -116,18 +116,9 @@ public fun PropertyEditor(
|
||||
}
|
||||
}
|
||||
|
||||
Button({
|
||||
classes(TreeStyles.propertyEditorButton)
|
||||
if (editorPropertyState != EditorPropertyState.Defined) {
|
||||
disabled()
|
||||
} else {
|
||||
onClick {
|
||||
meta.remove(name)
|
||||
update()
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text("\u00D7")
|
||||
CloseButton(editorPropertyState != EditorPropertyState.Defined){
|
||||
rootMeta.remove(name)
|
||||
update()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +130,7 @@ public fun PropertyEditor(
|
||||
Div({
|
||||
classes(TreeStyles.treeItem)
|
||||
}) {
|
||||
PropertyEditor(scope, meta, getPropertyState, updates, name + token, descriptor, expanded)
|
||||
PropertyEditor(scope, rootMeta, getPropertyState, updates, name + token, rootDescriptor, expanded)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,7 +146,7 @@ public fun PropertyEditor(
|
||||
) {
|
||||
PropertyEditor(
|
||||
scope = scope,
|
||||
meta = properties,
|
||||
rootMeta = properties,
|
||||
getPropertyState = { name ->
|
||||
if (properties[name] != null) {
|
||||
EditorPropertyState.Defined
|
||||
@ -172,9 +163,7 @@ public fun PropertyEditor(
|
||||
}
|
||||
}
|
||||
|
||||
invokeOnClose {
|
||||
properties.removeListener(scope)
|
||||
}
|
||||
awaitClose { properties.removeListener(scope) }
|
||||
},
|
||||
name = Name.EMPTY,
|
||||
rootDescriptor = descriptor,
|
||||
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.*
|
||||
import app.softwork.bootstrapcompose.Card
|
||||
import app.softwork.bootstrapcompose.NavbarLink
|
||||
import app.softwork.bootstrapcompose.Styling
|
||||
import org.jetbrains.compose.web.css.overflowY
|
||||
import org.jetbrains.compose.web.dom.*
|
||||
import org.w3c.dom.HTMLAnchorElement
|
||||
import org.w3c.dom.HTMLDivElement
|
||||
@ -51,6 +52,11 @@ public fun Tabs(
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
bodyAttrs = {
|
||||
style {
|
||||
overflowY("auto")
|
||||
}
|
||||
}
|
||||
) {
|
||||
activeTab?.content?.invoke(this)
|
||||
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.*
|
||||
import org.jetbrains.compose.web.css.Color
|
||||
import org.jetbrains.compose.web.css.color
|
||||
import org.jetbrains.compose.web.css.cursor
|
||||
import org.jetbrains.compose.web.css.textDecorationLine
|
||||
import org.jetbrains.compose.web.dom.Div
|
||||
import org.jetbrains.compose.web.dom.Span
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
@ -15,8 +14,6 @@ import space.kscience.dataforge.names.startsWith
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.VisionGroup
|
||||
import space.kscience.visionforge.asSequence
|
||||
import space.kscience.visionforge.compose.TreeStyles.hover
|
||||
import space.kscience.visionforge.compose.TreeStyles.invoke
|
||||
import space.kscience.visionforge.isEmpty
|
||||
|
||||
|
||||
@ -35,10 +32,6 @@ private fun TreeLabel(
|
||||
style {
|
||||
color(Color("#069"))
|
||||
cursor("pointer")
|
||||
hover.invoke {
|
||||
textDecorationLine("underline")
|
||||
}
|
||||
|
||||
}
|
||||
onClick { clickCallback(name) }
|
||||
}) {
|
||||
|
@ -4,14 +4,10 @@ package space.kscience.visionforge.compose
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import org.jetbrains.compose.web.attributes.*
|
||||
import org.jetbrains.compose.web.css.percent
|
||||
import org.jetbrains.compose.web.css.px
|
||||
import org.jetbrains.compose.web.css.width
|
||||
import org.jetbrains.compose.web.dom.Input
|
||||
import org.jetbrains.compose.web.dom.Option
|
||||
import org.jetbrains.compose.web.dom.Select
|
||||
import org.jetbrains.compose.web.dom.Text
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.HTMLOptionElement
|
||||
import org.w3c.dom.asList
|
||||
import space.kscience.dataforge.meta.*
|
||||
@ -29,20 +25,16 @@ public fun StringValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var stringValue by remember { mutableStateOf(value?.string ?: "") }
|
||||
var stringValue by remember(value, descriptor) { mutableStateOf(value?.string ?: "") }
|
||||
Input(type = InputType.Text) {
|
||||
style {
|
||||
width(100.percent)
|
||||
}
|
||||
classes("w-100")
|
||||
value(stringValue)
|
||||
onKeyDown { event ->
|
||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||
stringValue = (event.target as HTMLInputElement).value
|
||||
onValueChange(stringValue.asValue())
|
||||
}
|
||||
onChange { event ->
|
||||
stringValue = event.value
|
||||
}
|
||||
onChange {
|
||||
stringValue = it.target.value
|
||||
onInput { event ->
|
||||
stringValue = event.value
|
||||
onValueChange(event.value.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,16 +47,18 @@ public fun BooleanValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var innerValue by remember(value, descriptor) {
|
||||
mutableStateOf(
|
||||
value?.boolean ?: descriptor?.defaultValue?.boolean
|
||||
)
|
||||
}
|
||||
Input(type = InputType.Checkbox) {
|
||||
style {
|
||||
width(100.percent)
|
||||
}
|
||||
//this.attributes["indeterminate"] = (props.item == null).toString()
|
||||
checked(value?.boolean ?: false)
|
||||
classes("w-100")
|
||||
checked(innerValue ?: false)
|
||||
|
||||
onChange {
|
||||
val newValue = it.target.checked
|
||||
onValueChange(newValue.asValue())
|
||||
onInput { event ->
|
||||
innerValue = event.value
|
||||
onValueChange(event.value.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,25 +70,18 @@ public fun NumberValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var innerValue by remember { mutableStateOf(value?.string ?: "") }
|
||||
var innerValue by remember(value, descriptor) { mutableStateOf(value?.number) }
|
||||
Input(type = InputType.Number) {
|
||||
style {
|
||||
width(100.percent)
|
||||
classes("w-100")
|
||||
|
||||
value(innerValue ?: descriptor?.defaultValue?.number ?: 0.0)
|
||||
|
||||
onChange { event ->
|
||||
innerValue = event.value
|
||||
}
|
||||
value(innerValue)
|
||||
onKeyDown { event ->
|
||||
if (event.type == "keydown" && event.asDynamic().key == "Enter") {
|
||||
innerValue = (event.target as HTMLInputElement).value
|
||||
val number = innerValue.toDoubleOrNull()
|
||||
if (number == null) {
|
||||
console.error("The input value $innerValue is not a number")
|
||||
} else {
|
||||
onValueChange(number.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
onChange {
|
||||
innerValue = it.target.value
|
||||
onInput { event ->
|
||||
innerValue = event.value
|
||||
onValueChange(event.value?.asValue())
|
||||
}
|
||||
descriptor?.attributes?.get("step").number?.let {
|
||||
step(it)
|
||||
@ -116,11 +103,10 @@ public fun ComboValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var selected by remember { mutableStateOf(value?.string ?: "") }
|
||||
var selected by remember(value, descriptor) { mutableStateOf(value?.string ?: "") }
|
||||
Select({
|
||||
style {
|
||||
width(100.percent)
|
||||
}
|
||||
classes("w-100")
|
||||
|
||||
onChange {
|
||||
selected = it.target.value
|
||||
onValueChange(selected.asValue())
|
||||
@ -142,11 +128,11 @@ public fun ColorValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var innerValue by remember { mutableStateOf<String?>(value?.string ?: descriptor?.defaultValue?.string) }
|
||||
|
||||
Input(type = InputType.Color) {
|
||||
style {
|
||||
width(100.percent)
|
||||
marginAll(0.px)
|
||||
}
|
||||
classes("w-100")
|
||||
|
||||
value(
|
||||
value?.let { value ->
|
||||
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
|
||||
@ -154,8 +140,12 @@ public fun ColorValueChooser(
|
||||
//else "#" + Color(value.string).getHexString()
|
||||
} ?: "#000000"
|
||||
)
|
||||
onChange {
|
||||
onValueChange(it.target.value.asValue())
|
||||
onChange { event ->
|
||||
innerValue = event.value
|
||||
}
|
||||
onInput { event ->
|
||||
innerValue = event.value
|
||||
onValueChange(event.value.asValue())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,7 +184,7 @@ public fun RangeValueChooser(
|
||||
value: Value?,
|
||||
onValueChange: (Value?) -> Unit,
|
||||
) {
|
||||
var innerValue by remember { mutableStateOf(value?.double) }
|
||||
var innerValue by remember(value, descriptor) { mutableStateOf(value?.double) }
|
||||
var rangeDisabled: Boolean by remember { mutableStateOf(state != EditorPropertyState.Defined) }
|
||||
|
||||
|
||||
@ -219,9 +209,8 @@ public fun RangeValueChooser(
|
||||
}
|
||||
|
||||
Input(type = InputType.Range) {
|
||||
style {
|
||||
width(100.percent)
|
||||
}
|
||||
classes("w-100")
|
||||
|
||||
if (rangeDisabled) disabled()
|
||||
value(innerValue?.toString() ?: "")
|
||||
onChange {
|
||||
|
@ -14,7 +14,6 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.request
|
||||
import space.kscience.dataforge.names.Name
|
||||
import space.kscience.dataforge.names.isEmpty
|
||||
import space.kscience.visionforge.Vision
|
||||
import space.kscience.visionforge.compose.*
|
||||
import space.kscience.visionforge.root
|
||||
import space.kscience.visionforge.solid.Solid
|
||||
@ -81,15 +80,6 @@ public fun ThreeView(
|
||||
}
|
||||
}
|
||||
|
||||
val selectedVision: Vision? by derivedStateOf {
|
||||
selected?.let {
|
||||
when {
|
||||
it.isEmpty() -> solid
|
||||
else -> (solid as? SolidGroup)?.get(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (optionsSnapshot.controls.enabled) {
|
||||
|
||||
Row(
|
||||
@ -147,47 +137,57 @@ public fun ThreeView(
|
||||
SimpleThreeView(solids.context, optionsSnapshot, solid, selected)
|
||||
}
|
||||
|
||||
selectedVision?.let { vision ->
|
||||
Card(
|
||||
attrs = {
|
||||
style {
|
||||
position(Position.Absolute)
|
||||
top(5.px)
|
||||
right(5.px)
|
||||
width(450.px)
|
||||
}
|
||||
},
|
||||
headerAttrs = {
|
||||
// border = true
|
||||
},
|
||||
header = {
|
||||
NameCrumbs(selected) { selected = it }
|
||||
},
|
||||
footer = {
|
||||
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
|
||||
P {
|
||||
B { Text("Styles: ") }
|
||||
Text(styles.joinToString(separator = ", "))
|
||||
}
|
||||
}
|
||||
key(selected) {
|
||||
selected?.let {
|
||||
when {
|
||||
it.isEmpty() -> solid
|
||||
else -> (solid as? SolidGroup)?.get(it)
|
||||
}
|
||||
) {
|
||||
PropertyEditor(
|
||||
scope = solids.context,
|
||||
meta = vision.properties.root(),
|
||||
getPropertyState = { name ->
|
||||
if (vision.properties.own?.get(name) != null) {
|
||||
EditorPropertyState.Defined
|
||||
} else if (vision.properties.root()[name] != null) {
|
||||
// TODO differentiate
|
||||
EditorPropertyState.Default()
|
||||
} else {
|
||||
EditorPropertyState.Undefined
|
||||
}?.let { vision ->
|
||||
Card(
|
||||
attrs = {
|
||||
style {
|
||||
position(Position.Absolute)
|
||||
top(5.px)
|
||||
right(5.px)
|
||||
width(450.px)
|
||||
overflowY("auto")
|
||||
}
|
||||
},
|
||||
updates = vision.properties.changes,
|
||||
rootDescriptor = vision.descriptor
|
||||
)
|
||||
headerAttrs = {
|
||||
style {
|
||||
alignItems(AlignItems.Center)
|
||||
}
|
||||
},
|
||||
header = {
|
||||
NameCrumbs(selected) { selected = it }
|
||||
},
|
||||
footer = {
|
||||
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
|
||||
P {
|
||||
B { Text("Styles: ") }
|
||||
Text(styles.joinToString(separator = ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
PropertyEditor(
|
||||
scope = solids.context,
|
||||
rootMeta = vision.properties.root(),
|
||||
getPropertyState = { name ->
|
||||
if (vision.properties.own?.get(name) != null) {
|
||||
EditorPropertyState.Defined
|
||||
} else if (vision.properties.root()[name] != null) {
|
||||
// TODO differentiate
|
||||
EditorPropertyState.Default()
|
||||
} else {
|
||||
EditorPropertyState.Undefined
|
||||
}
|
||||
},
|
||||
updates = vision.properties.changes,
|
||||
rootDescriptor = vision.descriptor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +204,6 @@ public fun ThreeView(
|
||||
paddingAll(4.px)
|
||||
minWidth(400.px)
|
||||
height(100.percent)
|
||||
overflowY("auto")
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
Loading…
Reference in New Issue
Block a user