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 21, 2023
1 parent ed71ba9 commit e6bdb67
Show file tree
Hide file tree
Showing 8 changed files with 517 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package space.kscience.visionforge.compose

import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.dom.Li
import org.jetbrains.compose.web.dom.Nav
import org.jetbrains.compose.web.dom.Ol
import org.jetbrains.compose.web.dom.Text
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.length

@Composable
public fun NameCrumbs(name: Name?, link: (Name) -> Unit): Unit = Nav({
attr("aria-label","breadcrumb")
}) {
Ol({classes("breadcrumb")}) {
Li({
classes("breadcrumb-item")
onClick {
link(Name.EMPTY)
}
}) {
Text("\u2302")
}

if (name != null) {
val tokens = ArrayList<NameToken>(name.length)
name.tokens.forEach { token ->
tokens.add(token)
val fullName = Name(tokens.toList())
Text(".")
Li({
classes("breadcrumb-item")
if(tokens.size == name.length) classes("active")
onClick {
link(fullName)
}
}) {
Text(token.toString())
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ 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.dataforge.names.*
import space.kscience.visionforge.hidden


Expand All @@ -39,19 +36,17 @@ 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
*/
@Composable
private fun PropertyEditorItem(
/**
* Root config object - always non-null
*/
public fun PropertyEditor(
scope: CoroutineScope,
meta: MutableMeta,
getPropertyState: (Name) -> EditorPropertyState,
scope: CoroutineScope,
updates: Flow<Name>,
name: Name,
rootDescriptor: MetaDescriptor?,
name: Name = Name.EMPTY,
rootDescriptor: MetaDescriptor? = null,
initialExpanded: Boolean? = null,
) {
var expanded: Boolean by remember { mutableStateOf(initialExpanded ?: true) }
Expand Down Expand Up @@ -145,7 +140,7 @@ private fun PropertyEditorItem(
Div({
classes(TreeStyles.treeItem)
}) {
PropertyEditorItem(meta, getPropertyState, scope, updates, name, descriptor, expanded)
PropertyEditor(scope, meta, getPropertyState, updates, name + token, descriptor, expanded)
}
}
}
Expand All @@ -159,7 +154,8 @@ public fun PropertyEditor(
descriptor: MetaDescriptor? = null,
expanded: Boolean? = null,
) {
PropertyEditorItem(
PropertyEditor(
scope = scope,
meta = properties,
getPropertyState = { name ->
if (properties[name] != null) {
Expand All @@ -170,7 +166,6 @@ public fun PropertyEditor(
EditorPropertyState.Undefined
}
},
scope = scope,
updates = callbackFlow {
properties.onChange(scope) { name ->
scope.launch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package space.kscience.visionforge.compose

import androidx.compose.runtime.*
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLLIElement


public class ComposeTab(
public val key: String,
public val title: String,
public val content: ContentBuilder<HTMLDivElement>,
public val disabled: Boolean,
public val titleExt: ContentBuilder<HTMLLIElement>,
)

@Composable
public fun Tabs(tabs: List<ComposeTab>, activeKey: String) {
var active by remember(activeKey) { mutableStateOf(activeKey) }

Div({ classes("card", "text-center") }) {
Div({ classes("card-header") }) {

Ul({ classes("nav", "nav-tabs", "card-header-tabs") }) {
tabs.forEach { tab ->
Li({
classes("nav-item")
}) {
A(attrs = {
classes("nav-link")
if (active == tab.key) {
classes("active")
}
if (tab.disabled) {
classes("disabled")
}
onClick {
active = tab.key
}
}) {
Text(tab.title)
}
tab.titleExt.invoke(this)
}
}
}
}
tabs.find { it.key == active }?.let { tab ->
Div({ classes("card-body") }) {
tab.content.invoke(this)
}
}
}


}

public class TabBuilder internal constructor(public val key: String) {
private var title: String = key
public var disabled: Boolean = false
private var content: ContentBuilder<HTMLDivElement> = {}
private var titleExt: ContentBuilder<HTMLLIElement> = {}

@Composable
public fun Content(content: ContentBuilder<HTMLDivElement>) {
this.content = content
}

@Composable
public fun Title(title: String, titleExt: ContentBuilder<HTMLLIElement> = {}) {
this.title = title
this.titleExt = titleExt
}

internal fun build(): ComposeTab = ComposeTab(
key,
title,
content,
disabled,
titleExt
)
}

public class TabsBuilder {
public var active: String = ""
internal val tabs: MutableList<ComposeTab> = mutableListOf()

@Composable
public fun Tab(key: String, builder: @Composable TabBuilder.() -> Unit) {
tabs.add(TabBuilder(key).apply { builder() }.build())
}

public fun addTab(tab: ComposeTab) {
tabs.add(tab)
}
}

@Composable
public fun Tabs(builder: @Composable TabsBuilder.() -> Unit) {
val result = TabsBuilder().apply { builder() }
Tabs(result.tabs, result.active)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package space.kscience.visionforge.compose

import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Button
import org.jetbrains.compose.web.dom.Text
import org.w3c.files.Blob
import org.w3c.files.BlobPropertyBag
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Vision
import space.kscience.visionforge.encodeToString
import space.kscience.visionforge.solid.specifications.Canvas3DOptions

@Composable
internal fun CanvasControls(
vision: Vision?,
options: Canvas3DOptions,
) {
FlexColumn {
FlexRow({
style {
border {
width(1.px)
style(LineStyle.Solid)
color(Color("blue"))
}
padding(4.px)
}
}) {
vision?.let { vision ->
Button({
onClick { event ->
val json = vision.encodeToString()
event.stopPropagation();
event.preventDefault();

val fileSaver = kotlinext.js.require<dynamic>("file-saver")
val blob = Blob(arrayOf(json), BlobPropertyBag("text/json;charset=utf-8"))
fileSaver.saveAs(blob, "object.json") as Unit
}
}) {
Text("Export")
}
}
}
PropertyEditor(
scope = vision?.manager?.context ?: Global,
properties = options.meta,
descriptor = Canvas3DOptions.descriptor,
expanded = false
)

}
}


@Composable
public fun ThreeControls(
vision: Vision?,
canvasOptions: Canvas3DOptions,
selected: Name?,
onSelect: (Name?) -> Unit,
tabBuilder: @Composable TabsBuilder.() -> Unit = {},
) {
Tabs {
active = "Tree"
vision?.let { vision ->
Tab("Tree") {
CardTitle("Vision tree")
VisionTree(vision, Name.EMPTY, selected, onSelect)
}
}
Tab("Settings") {
CardTitle("Canvas configuration")
CanvasControls(vision, canvasOptions)
}
tabBuilder()
}
}
Loading

0 comments on commit e6bdb67

Please sign in to comment.