Skip to content

Commit

Permalink
Add compose-html
Browse files Browse the repository at this point in the history
  • Loading branch information
altavir committed Nov 20, 2023
1 parent f0048a4 commit ed71ba9
Show file tree
Hide file tree
Showing 9 changed files with 612 additions and 99 deletions.
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ include(
":ui:ring",
// ":ui:material",
":ui:bootstrap",
// ":ui:compose",
":ui:compose",
":visionforge-core",
":visionforge-solid",
// ":visionforge-fx",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package space.kscience.visionforge.compose

import androidx.compose.runtime.*
import kotlinx.html.js.onClickFunction
import org.jetbrains.compose.web.css.AlignItems
import org.jetbrains.compose.web.css.alignItems
import org.jetbrains.compose.web.dom.A
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.Span
import org.w3c.dom.events.Event
import org.jetbrains.compose.web.dom.Text
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.get
Expand All @@ -17,10 +18,6 @@ import space.kscience.dataforge.names.lastOrNull
import space.kscience.dataforge.names.plus


private val MetaViewerItem: FC<MetaViewerProps> = fc("MetaViewerItem") { props ->
metaViewerItem(props)
}

@Composable
private fun MetaViewerItem(root: Meta, name: Name, rootDescriptor: MetaDescriptor? = null) {
var expanded: Boolean by remember { mutableStateOf(true) }
Expand All @@ -29,11 +26,7 @@ private fun MetaViewerItem(root: Meta, name: Name, rootDescriptor: MetaDescripto
val actualValue = item?.value ?: descriptorItem?.defaultValue
val actualMeta = item ?: descriptorItem?.defaultNode

val token = name.lastOrNull()?.toString() ?: props.rootName ?: ""

val expanderClick: (Event) -> Unit = {
expanded = !expanded
}
val token = name.lastOrNull()?.toString() ?: ""

FlexRow(attrs = {
classes("metaItem")
Expand All @@ -42,42 +35,34 @@ private fun MetaViewerItem(root: Meta, name: Name, rootDescriptor: MetaDescripto
}
}) {
if (actualMeta?.isLeaf == false) {
Span(attrs = {

})
styledSpan {
css {
+TreeStyles.treeCaret
if (expanded) {
+TreeStyles.treeCaredDown
}
Span({
classes(TreeStyles.treeCaret)
if (expanded) {
classes(TreeStyles.treeCaretDown)
}
attrs {
onClickFunction = expanderClick
}
}
onClick { expanded = !expanded }
})
}

styledSpan {
css {
+TreeStyles.treeLabel
if (item == null) {
+TreeStyles.treeLabelInactive
}
Span({
classes(TreeStyles.treeLabel)
if (item == null) {
classes(TreeStyles.treeLabelInactive)
}
+token
}) {
Text(token)
}
styledDiv {
a {
+actualValue.toString()

Div {
A {
Text(actualValue.toString())
}
}
}
if (expanded) {
flexColumn {
css {
+TreeStyles.tree
}
FlexColumn({
classes(TreeStyles.tree)
}) {
val keys = buildSet {
descriptorItem?.children?.keys?.forEach {
add(NameToken(it))
Expand All @@ -86,45 +71,17 @@ private fun MetaViewerItem(root: Meta, name: Name, rootDescriptor: MetaDescripto
}

keys.filter { !it.body.startsWith("@") }.forEach { token ->
styledDiv {
css {
+TreeStyles.treeItem
}
child(MetaViewerItem) {
attrs {
this.key = props.name.toString()
this.root = props.root
this.name = props.name + token
this.descriptor = props.descriptor
}
}
//configEditor(props.root, props.name + token, props.descriptor, props.default)
Div({
classes(TreeStyles.treeItem)
}) {
MetaViewerItem(root, name + token, rootDescriptor)
}
}
}
}


}

@JsExport
public val MetaViewer: FC<MetaViewerProps> = fc("MetaViewer") { props ->
child(MetaViewerItem) {
attrs {
this.key = ""
this.root = props.root
this.name = Name.EMPTY
this.descriptor = props.descriptor
}
}
}

public fun RBuilder.metaViewer(meta: Meta, descriptor: MetaDescriptor? = null, key: Any? = null) {
child(MetaViewer) {
attrs {
this.key = key?.toString() ?: ""
this.root = meta
this.descriptor = descriptor
}
}
@Composable
public fun MetaViewer(meta: Meta, descriptor: MetaDescriptor? = null) {
MetaViewerItem(meta, Name.EMPTY, descriptor)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package space.kscience.visionforge.compose

import androidx.compose.runtime.*
import kotlinx.coroutines.CoroutineScope
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
import space.kscience.dataforge.meta.MutableMeta
import space.kscience.dataforge.meta.ObservableMutableMeta
import space.kscience.dataforge.meta.descriptors.MetaDescriptor
import space.kscience.dataforge.meta.descriptors.ValueRequirement
import space.kscience.dataforge.meta.descriptors.get
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.remove
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.lastOrNull
import space.kscience.visionforge.hidden


/**
* The display state of a property
*/
public sealed class EditorPropertyState {
public object Defined : EditorPropertyState()
public class Default(public val source: String = "unknown") : EditorPropertyState()
public object Undefined : EditorPropertyState()

}

/**
* @param rootDescriptor Full path to the displayed node in [meta]. Could be empty
*/
@Composable
private fun PropertyEditorItem(
/**
* Root config object - always non-null
*/
meta: MutableMeta,
getPropertyState: (Name) -> EditorPropertyState,
scope: CoroutineScope,
updates: Flow<Name>,
name: Name,
rootDescriptor: MetaDescriptor?,
initialExpanded: Boolean? = null,
) {
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 editorPropertyState: EditorPropertyState by remember { mutableStateOf(getPropertyState(name)) }


val keys = remember(descriptor) {
buildSet {
descriptor?.children?.filterNot {
it.key.startsWith("@") || it.value.hidden
}?.forEach {
add(NameToken(it.key))
}
//ownProperty?.items?.keys?.filterNot { it.body.startsWith("@") }?.let { addAll(it) }
}
}

val token = name.lastOrNull()?.toString() ?: "Properties"

fun update() {
property = meta.getOrCreate(name)
editorPropertyState = getPropertyState(name)
}

LaunchedEffect(meta) {
updates.collect { updatedName ->
if (updatedName == name) {
update()
}
}
}

FlexRow({
style {
alignItems(AlignItems.Center)
}
}) {
if (keys.isNotEmpty()) {
Span({
classes(TreeStyles.treeCaret)
if (expanded) {
classes(TreeStyles.treeCaretDown)
}
onClick { expanded = !expanded }
})
}
Span({
classes(TreeStyles.treeLabel)
if (editorPropertyState != EditorPropertyState.Defined) {
classes(TreeStyles.treeLabelInactive)
}
}) {
Text(token)
}

if (!name.isEmpty() && descriptor?.valueRequirement != ValueRequirement.ABSENT) {
Div({
style {
width(160.px)
marginAll(1.px, 5.px)
}
}) {
ValueChooser(descriptor, editorPropertyState, property.value) {
property.value = it
editorPropertyState = getPropertyState(name)
}
}

Button({
classes(TreeStyles.propertyEditorButton)
if (editorPropertyState != EditorPropertyState.Defined) {
disabled()
} else {
onClick {
meta.remove(name)
update()
}
}
}) {
Text("\u00D7")
}
}
}
if (expanded) {
FlexColumn({
classes(TreeStyles.tree)
}) {
keys.forEach { token ->
Div({
classes(TreeStyles.treeItem)
}) {
PropertyEditorItem(meta, getPropertyState, scope, updates, name, descriptor, expanded)
}
}
}
}
}

@Composable
public fun PropertyEditor(
scope: CoroutineScope,
properties: ObservableMutableMeta,
descriptor: MetaDescriptor? = null,
expanded: Boolean? = null,
) {
PropertyEditorItem(
meta = properties,
getPropertyState = { name ->
if (properties[name] != null) {
EditorPropertyState.Defined
} else if (descriptor?.get(name)?.defaultValue != null) {
EditorPropertyState.Default("descriptor")
} else {
EditorPropertyState.Undefined
}
},
scope = scope,
updates = callbackFlow {
properties.onChange(scope) { name ->
scope.launch {
send(name)
}
}

invokeOnClose {
properties.removeListener(scope)
}
},
name = Name.EMPTY,
rootDescriptor = descriptor,
initialExpanded = expanded,
)
}
Loading

0 comments on commit ed71ba9

Please sign in to comment.