More or less working muon monitor

This commit is contained in:
Alexander Nozik 2023-12-27 12:01:55 +03:00
parent 72ead21ef0
commit 659b9c3525
37 changed files with 370 additions and 364 deletions

View File

@ -1,50 +0,0 @@
@file:JsModule("react-file-drop")
@file:JsNonModule
package drop
import org.w3c.dom.DragEvent
import org.w3c.files.FileList
import react.Component
import react.Props
import react.State
sealed external class DropEffects {
@JsName("copy")
object Copy : DropEffects
@JsName("move")
object Move : DropEffects
@JsName("link")
object Link : DropEffects
@JsName("none")
object None : DropEffects
}
external interface FileDropProps : Props {
var className: String?
var targetClassName: String?
var draggingOverFrameClassName: String?
var draggingOverTargetClassName: String?
// var frame?: Exclude<HTMLElementTagNameMap[keyof HTMLElementTagNameMap], HTMLElement> | HTMLDocument;
var onFrameDragEnter: ((event: DragEvent) -> Unit)?
var onFrameDragLeave: ((event: DragEvent) -> Unit)?
var onFrameDrop: ((event: DragEvent) -> Unit)?
// var onDragOver: ReactDragEventHandler<HTMLDivElement>?
// var onDragLeave: ReactDragEventHandler<HTMLDivElement>?
var onDrop: ((files: FileList?, event: dynamic) -> Unit)?//event:DragEvent<HTMLDivElement>)
var dropEffect: DropEffects?
}
external interface FileDropState : State {
var draggingOverFrame: Boolean
var draggingOverTarget: Boolean
}
external class FileDrop : Component<FileDropProps, FileDropState> {
override fun render(): dynamic
}

View File

@ -0,0 +1,87 @@
@file:OptIn(ExperimentalComposeWebApi::class)
package space.kscience.visionforge.gdml.demo
import androidx.compose.runtime.*
import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.attributes.name
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.I
import org.jetbrains.compose.web.dom.Input
import org.jetbrains.compose.web.dom.Text
import org.w3c.files.FileList
//https://codepen.io/zahedkamal87/pen/PobNNwE
@Composable
fun FileDrop(
title: String = "Drop files or Click here to select files to upload.",
onFileDrop: (FileList) -> Unit,
) {
var dragOver by remember { mutableStateOf(false) }
Div({
id("dropzone")
style {
border(
width = 0.2.cssRem,
style = LineStyle.Dashed,
color = Color("#6583fe")
)
padding(2.cssRem)
borderRadius(0.25.cssRem)
backgroundColor(Color("#fff"))
textAlign("center")
fontSize(1.5.cssRem)
transitions {
all {
delay(0.25.s)
timingFunction(AnimationTimingFunction.EaseInOut)
properties("background-color")
}
}
cursor("pointer")
}
listOf("drag", "dragstart", "dragend", "dragenter").forEach {
addEventListener(it) { event ->
event.preventDefault()
event.stopPropagation()
}
}
onDragOver { event ->
event.preventDefault()
event.stopPropagation()
dragOver = true
}
onDragLeave { event ->
event.preventDefault()
event.stopPropagation()
dragOver = false
}
onDrop { event ->
event.preventDefault()
event.stopPropagation()
dragOver = false
event.dataTransfer?.files?.let {
onFileDrop(it)
}
}
}) {
I({ classes("bi", "bi-cloud-upload", "dropzone-icon") })
Text(title)
Input(type = InputType.File, attrs = {
style {
display(DisplayStyle.None)
}
classes("dropzone-input")
name("files")
})
}
}
//
//dropzone.addEventListener("click", function(e) {
// dropzone_input.click();
//});

View File

@ -69,8 +69,8 @@ fun GDMLApp(solids: Solids, initialVision: Solid?, selected: Name? = null) {
H2 { H2 {
Text("Drag and drop .gdml or .json VisionForge files here") Text("Drag and drop .gdml or .json VisionForge files here")
} }
fileDrop("(drag file here)") { files -> FileDrop("(drag file here)") { files ->
val file = files?.get(0) val file = files[0]
if (file != null) { if (file != null) {
readFileAsync(file) readFileAsync(file)
} }

View File

@ -1,30 +0,0 @@
package space.kscience.visionforge.gdml.demo
import drop.FileDrop
import kotlinx.css.*
import org.w3c.files.FileList
import react.RBuilder
import styled.css
import styled.styledDiv
//TODO move styles to inline
fun RBuilder.fileDrop(title: String, action: (files: FileList?) -> Unit) {
styledDiv {
css {
border = Border(style = BorderStyle.dashed, width = 1.px, color = Color.orange)
flexGrow = 0.0
alignContent = Align.center
}
child(FileDrop::class) {
attrs {
onDrop = { files, _ ->
console.info("loaded $files")
action(files)
}
}
+title
}
}
}

View File

@ -1,3 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
config.module.rules.push(...ringConfig.module.rules)

View File

@ -1,3 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
config.module.rules.push(...ringConfig.module.rules)

View File

@ -1,5 +1,6 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose)
application application
} }
@ -14,11 +15,22 @@ kscience {
fullStack( fullStack(
"muon-monitor.js", "muon-monitor.js",
jvmConfig = { withJava() }, jvmConfig = { withJava() },
jsConfig = { useCommonJs() } // jsConfig = { useCommonJs() },
browserConfig = {
webpackTask{
cssSupport{
enabled = true
}
scssSupport{
enabled = true
}
}
}
) )
commonMain { commonMain {
implementation(projects.visionforgeSolid) implementation(projects.visionforgeSolid)
implementation(projects.visionforgeComposeHtml)
} }
jvmMain { jvmMain {
implementation("org.apache.commons:commons-math3:3.6.1") implementation("org.apache.commons:commons-math3:3.6.1")

View File

@ -4,12 +4,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import app.softwork.bootstrapcompose.Button import app.softwork.bootstrapcompose.Button
import app.softwork.bootstrapcompose.ButtonGroup
import app.softwork.bootstrapcompose.Container
import kotlinx.browser.window import kotlinx.browser.window
import kotlinx.coroutines.await import kotlinx.coroutines.await
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.P import org.jetbrains.compose.web.dom.P
import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Span
import org.jetbrains.compose.web.dom.Text import org.jetbrains.compose.web.dom.Text
@ -17,8 +18,6 @@ import org.w3c.fetch.RequestInit
import space.kscience.dataforge.meta.invoke import space.kscience.dataforge.meta.invoke
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.visionforge.Colors import space.kscience.visionforge.Colors
import space.kscience.visionforge.compose.FlexColumn
import space.kscience.visionforge.compose.FlexRow
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.ambientLight import space.kscience.visionforge.solid.ambientLight
import space.kscience.visionforge.solid.edges import space.kscience.visionforge.solid.edges
@ -51,16 +50,21 @@ fun MMApp(solids: Solids, model: Model, selected: Name? = null) {
val events = remember { mutableStateListOf<Event>() } val events = remember { mutableStateListOf<Event>() }
Div({ Container(fluid = true,
attrs = {
style { style {
height(100.vh - 12.pt) height(100.vh - 12.pt)
} }
}) { }
ThreeView(solids, root, selected, mmOptions) { ) {
ThreeView(
solids = solids,
solid = root,
initialSelected = selected,
options = mmOptions,
sidebarTabs = {
Tab("Events") { Tab("Events") {
ButtonGroup {
FlexColumn {
FlexRow {
Button("Next") { Button("Next") {
solids.context.launch { solids.context.launch {
val event = window.fetch( val event = window.fetch(
@ -84,7 +88,7 @@ fun MMApp(solids: Solids, model: Model, selected: Name? = null) {
model.reset() model.reset()
} }
} }
}
events.forEach { event -> events.forEach { event ->
P { P {
Span { Span {
@ -102,5 +106,6 @@ fun MMApp(solids: Solids, model: Model, selected: Name? = null) {
} }
} }
} }
)
} }
} }

View File

@ -1,11 +1,13 @@
package ru.mipt.npm.muon.monitor package ru.mipt.npm.muon.monitor
import org.jetbrains.compose.web.css.Style
import org.jetbrains.compose.web.renderComposable import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Document import org.w3c.dom.Document
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.request import space.kscience.dataforge.context.request
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
import space.kscience.visionforge.VisionManager import space.kscience.visionforge.VisionManager
import space.kscience.visionforge.compose.VisionForgeStyles
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
@ -22,8 +24,8 @@ private class MMDemoApp : Application {
val model = Model(visionManager) val model = Model(visionManager)
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") renderComposable("app") {
renderComposable(element) { Style(VisionForgeStyles)
MMApp(context.request(Solids), model) MMApp(context.request(Solids), model)
} }
} }

View File

@ -1,3 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
config.module.rules.push(...ringConfig.module.rules)

View File

@ -16,6 +16,12 @@ kotlin {
js(IR) { js(IR) {
browser { browser {
webpackTask { webpackTask {
cssSupport{
enabled = true
}
scssSupport{
enabled = true
}
mainOutputFileName.set("js/visionforge-playground.js") mainOutputFileName.set("js/visionforge-playground.js")
} }
} }

View File

@ -1,23 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
const path = require('path');
config.module.rules.push(...ringConfig.module.rules)
config.module.rules.push(
{
test: /\.css$/,
exclude: [
path.resolve(__dirname, "../../node_modules/@jetbrains/ring-ui")
],
use: [
{
loader: 'style-loader',
options: {}
},
{
loader: 'css-loader',
options: {}
}
]
}
)

View File

@ -41,11 +41,11 @@ dependencyResolutionManagement {
include( include(
// ":ui", // ":ui",
":ui:react", // ":ui:react",
":ui:ring", // ":ui:ring",
// ":ui:material", // ":ui:material",
":ui:bootstrap", // ":ui:bootstrap",
":visionforge-compose", ":visionforge-compose-html",
":visionforge-core", ":visionforge-core",
":visionforge-solid", ":visionforge-solid",
// ":visionforge-fx", // ":visionforge-fx",

View File

@ -1,4 +1,3 @@
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
alias(spclibs.plugins.compose) alias(spclibs.plugins.compose)
@ -16,12 +15,12 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
api(projects.visionforgeCore) api(projects.visionforgeCore)
api(compose.runtime)
} }
} }
val jvmMain by getting { val jvmMain by getting {
dependencies { dependencies {
api(compose.runtime)
api(compose.foundation) api(compose.foundation)
api(compose.material) api(compose.material)
api(compose.preview) api(compose.preview)

View File

@ -0,0 +1,88 @@
package space.kscience.visionforge.compose
import androidx.compose.runtime.*
import app.softwork.bootstrapcompose.Card
import app.softwork.bootstrapcompose.NavbarLink
import app.softwork.bootstrapcompose.Styling
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLAnchorElement
import org.w3c.dom.HTMLDivElement
public class ComposeTab(
public val key: String,
public val title: ContentBuilder<HTMLAnchorElement>,
public val disabled: Boolean,
public val content: ContentBuilder<HTMLDivElement>,
)
@Composable
public fun Tabs(
tabs: List<ComposeTab>,
activeKey: String,
styling: (Styling.() -> Unit)? = null,
attrs: AttrBuilderContext<HTMLDivElement>? = null,
) {
var active by remember(activeKey) { mutableStateOf(activeKey) }
val activeTab by derivedStateOf { tabs.find { it.key == active } }
Card(
styling,
attrs,
header = {
Ul({ classes("nav", "nav-tabs", "card-header-tabs") }) {
tabs.forEach { tab ->
Li({
classes("nav-item")
}) {
NavbarLink(
active = active == tab.key,
disabled = tab.disabled,
attrs = {
onClick { event ->
event.preventDefault()
active = tab.key
}
}
) {
tab.title.invoke(this)
}
}
}
}
}
) {
activeTab?.content?.invoke(this)
}
}
public class TabsBuilder {
internal val tabs: MutableList<ComposeTab> = mutableListOf()
@Composable
public fun Tab(
key: String,
label: ContentBuilder<HTMLAnchorElement> = { Text(key) },
disabled: Boolean = false,
content: ContentBuilder<HTMLDivElement>,
) {
tabs.add(ComposeTab(key, label, disabled, content))
}
public fun addTab(tab: ComposeTab) {
tabs.add(tab)
}
}
@Composable
public fun Tabs(
activeKey: String? = null,
styling: (Styling.() -> Unit)? = null,
attrs: AttrBuilderContext<HTMLDivElement>? = null,
builder: @Composable TabsBuilder.() -> Unit,
) {
val result = TabsBuilder().apply { builder() }
Tabs(result.tabs, activeKey ?: result.tabs.firstOrNull()?.key ?: "", styling, attrs)
}

View File

@ -5,7 +5,7 @@ import org.jetbrains.compose.web.css.*
@OptIn(ExperimentalComposeWebApi::class) @OptIn(ExperimentalComposeWebApi::class)
public object TreeStyles : StyleSheet() { public object TreeStyles : StyleSheet(VisionForgeStyles) {
/** /**
* Remove default bullets * Remove default bullets
*/ */
@ -46,13 +46,11 @@ public object TreeStyles : StyleSheet() {
public val treeItem: String by style { public val treeItem: String by style {
alignItems(AlignItems.Center) alignItems(AlignItems.Center)
paddingLeft(10.px) paddingLeft(10.px)
border { property("border-left", CSSBorder().apply{
left {
width(1.px) width(1.px)
color(Color.lightgray) color(Color.lightgray)
style = LineStyle.Dashed style = LineStyle.Dashed
} })
}
} }
public val treeLabel: String by style { public val treeLabel: String by style {

View File

@ -0,0 +1,7 @@
package space.kscience.visionforge.compose
import org.jetbrains.compose.web.css.StyleSheet
public object VisionForgeStyles: StyleSheet() {
}

View File

@ -1,102 +0,0 @@
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)
}

View File

@ -7,6 +7,16 @@ description = "Jupyter api artifact including all common modules"
kscience { kscience {
fullStack( fullStack(
"js/visionforge-jupyter-common.js", "js/visionforge-jupyter-common.js",
browserConfig = {
webpackTask {
cssSupport{
enabled = true
}
scssSupport {
enabled = true
}
}
}
) )
dependencies { dependencies {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)

View File

@ -1,24 +0,0 @@
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
const path = require('path');
config.module.rules.push(...ringConfig.module.rules)
config.module.rules.push(
{
test: /\.css$/,
exclude: [
path.resolve(__dirname, "../../node_modules/@jetbrains/ring-ui")
],
use: [
{
loader: 'style-loader',
options: {}
},
{
loader: 'css-loader',
options: {}
}
]
}
)

View File

@ -7,8 +7,14 @@ val tablesVersion = "0.3.0"
kscience { kscience {
jvm() jvm()
js { js {
useCommonJs()
binaries.library() binaries.library()
browser {
webpackTask{
scssSupport {
enabled = true
}
}
}
} }
useSerialization() useSerialization()
@ -17,8 +23,8 @@ kscience {
api("space.kscience:tables-kt:${tablesVersion}") api("space.kscience:tables-kt:${tablesVersion}")
} }
jsMain { jsMain {
implementation(npm("tabulator-tables", "5.5.2")) api(npm("tabulator-tables", "5.5.2"))
implementation(npm("@types/tabulator-tables", "5.5.3")) api(npm("@types/tabulator-tables", "5.5.3"))
} }
} }

View File

@ -5,6 +5,7 @@
"NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING") "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING")
@file:JsModule("tabulator-tables") @file:JsModule("tabulator-tables")
@file:JsNonModule
package tabulator package tabulator

View File

@ -14,7 +14,7 @@ kscience {
commonMain { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeCompose) api(projects.visionforgeComposeHtml)
} }
jsMain { jsMain {

View File

@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import org.jetbrains.compose.web.renderComposable import org.jetbrains.compose.web.renderComposable
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*

View File

@ -1,6 +1,9 @@
package space.kscience.visionforge.solid.three.compose package space.kscience.visionforge.solid.three.compose
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import app.softwork.bootstrapcompose.Column
import app.softwork.bootstrapcompose.Layout.Height
import app.softwork.bootstrapcompose.Row
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.dom.Button import org.jetbrains.compose.web.dom.Button
import org.jetbrains.compose.web.dom.Text import org.jetbrains.compose.web.dom.Text
@ -18,8 +21,8 @@ internal fun CanvasControls(
vision: Vision?, vision: Vision?,
options: Canvas3DOptions, options: Canvas3DOptions,
) { ) {
FlexColumn { Column {
FlexRow({ Row(attrs = {
style { style {
border { border {
width(1.px) width(1.px)
@ -64,8 +67,11 @@ public fun ThreeControls(
onSelect: (Name?) -> Unit, onSelect: (Name?) -> Unit,
tabBuilder: @Composable TabsBuilder.() -> Unit = {}, tabBuilder: @Composable TabsBuilder.() -> Unit = {},
) { ) {
Tabs { Tabs(
active = "Tree" styling = {
Layout.height = Height.Full
}
) {
vision?.let { vision -> vision?.let { vision ->
Tab("Tree") { Tab("Tree") {
CardTitle("Vision tree") CardTitle("Vision tree")

View File

@ -2,6 +2,10 @@ package space.kscience.visionforge.solid.three.compose
import androidx.compose.runtime.* import androidx.compose.runtime.*
import app.softwork.bootstrapcompose.Card import app.softwork.bootstrapcompose.Card
import app.softwork.bootstrapcompose.Column
import app.softwork.bootstrapcompose.Layout.Height
import app.softwork.bootstrapcompose.Layout.Width
import app.softwork.bootstrapcompose.Row
import kotlinx.dom.clear import kotlinx.dom.clear
import org.jetbrains.compose.web.ExperimentalComposeWebApi import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.css.* import org.jetbrains.compose.web.css.*
@ -90,24 +94,27 @@ public fun ThreeView(
if (optionsSnapshot.controls.enabled) { if (optionsSnapshot.controls.enabled) {
Row(
FlexRow({ styling = {
style { Layout {
height(100.percent) width = Width.Full
width(100.percent) height = Height.Full
flexWrap(FlexWrap.Wrap)
alignItems(AlignItems.Stretch)
alignContent(AlignContent.Stretch)
} }
}) { }
FlexColumn({ ) {
Column(
styling = {
Layout {
height = Height.Full
}
},
attrs = {
style { style {
height(100.percent)
minWidth(600.px)
flex(10, 1, 600.px)
position(Position.Relative) position(Position.Relative)
minWidth(600.px)
} }
}) { }
) {
if (solid == null) { if (solid == null) {
Div({ Div({
style { style {
@ -143,20 +150,28 @@ public fun ThreeView(
} }
selectedVision?.let { vision -> selectedVision?.let { vision ->
Div({ Card(
attrs = {
style { style {
position(Position.Absolute) position(Position.Absolute)
top(5.px) top(5.px)
right(5.px) right(5.px)
width(450.px) width(450.px)
} }
}) { },
Card(
headerAttrs = { headerAttrs = {
// border = true // border = true
}, },
header = { header = {
NameCrumbs(selected) { selected = it } NameCrumbs(selected) { selected = it }
},
footer = {
vision.styles.takeIf { it.isNotEmpty() }?.let { styles ->
P {
B { Text("Styles: ") }
Text(styles.joinToString(separator = ", "))
}
}
} }
) { ) {
PropertyEditor( PropertyEditor(
@ -175,30 +190,29 @@ public fun ThreeView(
updates = vision.properties.changes, updates = vision.properties.changes,
rootDescriptor = vision.descriptor rootDescriptor = vision.descriptor
) )
}
}
} }
vision.styles.takeIf { it.isNotEmpty() }?.let { styles -> Column(
P { auto = true,
B { Text("Styles: ") } styling = {
Text(styles.joinToString(separator = ", ")) Layout {
height = Height.Full
} }
} },
} attrs = {
}
}
}
FlexColumn({
style { style {
paddingAll(4.px) paddingAll(4.px)
minWidth(400.px) minWidth(400.px)
height(100.percent) height(100.percent)
overflowY("auto") overflowY("auto")
flex(1, 10, 300.px)
} }
}) { }
) {
ThreeControls(solid, optionsSnapshot, selected, onSelect = { selected = it }, tabBuilder = sidebarTabs) ThreeControls(solid, optionsSnapshot, selected, onSelect = { selected = it }, tabBuilder = sidebarTabs)
} }
}
} else { } else {
SimpleThreeView(solids.context, optionsSnapshot, solid, selected) SimpleThreeView(solids.context, optionsSnapshot, solid, selected)
} }

View File

@ -10,7 +10,7 @@ kscience {
commonMain { commonMain {
api(projects.visionforgeSolid) api(projects.visionforgeSolid)
api(projects.visionforgeCompose) api(projects.visionforgeComposeHtml)
} }
jvmMain{ jvmMain{
@ -19,6 +19,8 @@ kscience {
jsMain{ jsMain{
api(projects.visionforgeThreejs) api(projects.visionforgeThreejs)
implementation(npm("file-saver","2.0.5"))
implementation(npm("@types/file-saver", "2.0.7"))
compileOnly(npm("webpack-bundle-analyzer","4.5.0")) compileOnly(npm("webpack-bundle-analyzer","4.5.0"))
} }
} }