Css revamp

This commit is contained in:
Alexander Nozik 2021-06-14 16:42:02 +03:00
parent 9cc3f65a18
commit ea5f0abbf6
15 changed files with 307 additions and 155 deletions

View File

@ -4,6 +4,7 @@ import kotlinx.browser.window
import org.w3c.files.FileReader import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
import react.* import react.*
import react.dom.h2
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.fetch import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
@ -12,9 +13,9 @@ import space.kscience.gdml.decodeFromString
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.ring.ThreeCanvasWithControls import space.kscience.visionforge.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.root
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids import space.kscience.visionforge.solid.Solids
import styled.styledDiv
external interface GDMLAppProps : RProps { external interface GDMLAppProps : RProps {
var context: Context var context: Context
@ -24,8 +25,8 @@ external interface GDMLAppProps : RProps {
@JsExport @JsExport
val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props -> val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
var vision: Solid? by useState { props.vision }
val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager } val visionManager = useMemo(props.context) { props.context.fetch(Solids).visionManager }
var vision: Solid? by useState { props.vision?.apply { root(visionManager) } }
fun loadData(name: String, data: String) { fun loadData(name: String, data: String) {
val parsedVision = when { val parsedVision = when {
@ -43,13 +44,15 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
vision = parsedVision as? Solid ?: error("Parsed vision is not a solid") vision = parsedVision as? Solid ?: error("Parsed vision is not a solid")
} }
styledDiv {
child(ThreeCanvasWithControls) { child(ThreeCanvasWithControls) {
attrs { attrs {
this.context = props.context this.context = props.context
this.solid = vision this.solid = vision
this.selected = props.selected this.selected = props.selected
tab("Load") { tab("Load") {
h2 {
+"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?.get(0)
if (file != null) { if (file != null) {
@ -64,7 +67,7 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
} }
} }
} }
}
} }
} }

View File

@ -1,28 +1,45 @@
package space.kscience.visionforge.gdml.demo package space.kscience.visionforge.gdml.demo
import kotlinx.browser.document import kotlinx.browser.document
import kotlinx.css.*
import react.child import react.child
import react.dom.render import react.dom.render
import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.Global
import space.kscience.gdml.GdmlShowCase import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.Application import space.kscience.visionforge.Application
import space.kscience.visionforge.bootstrap.useBootstrap
import space.kscience.visionforge.gdml.toVision import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.solid.three.ThreePlugin import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication import space.kscience.visionforge.startApplication
import styled.injectGlobal
private class GDMLDemoApp : Application { private class GDMLDemoApp : Application {
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
useBootstrap() val context = Global.buildContext("gdml-demo"){
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
val context = Global.buildContext("demo"){
plugin(ThreePlugin) plugin(ThreePlugin)
} }
injectGlobal {
html{
height = 100.pct
}
body{
height = 100.pct
display = Display.flex
alignItems = Align.stretch
}
"#application"{
width = 100.pct
display = Display.flex
alignItems = Align.stretch
}
}
val element = document.getElementById("application") ?: error("Element with id 'application' not found on page")
render(element) { render(element) {
child(GDMLApp) { child(GDMLApp) {
val vision = GdmlShowCase.cubes().toVision() val vision = GdmlShowCase.cubes().toVision()

View File

@ -8,6 +8,6 @@
<link rel="stylesheet" href="css/fileDrop.css"> <link rel="stylesheet" href="css/fileDrop.css">
</head> </head>
<body class="application"> <body class="application">
<div class="container-fluid max-vh-100" id = "app"> </div> <div id = "application"></div>
</body> </body>
</html> </html>

View File

@ -21,11 +21,6 @@ public external interface ThreeCanvasProps : RProps {
public var selected: Name? public var selected: Name?
} }
public external interface ThreeCanvasState : RState {
public var element: Element?
// var canvas: ThreeCanvas?
}
public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent( public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent(
"ThreeCanvasComponent" "ThreeCanvasComponent"
) { props -> ) { props ->
@ -55,8 +50,7 @@ public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functio
css { css {
maxWidth = 100.vw maxWidth = 100.vw
maxHeight = 100.vh maxHeight = 100.vh
display = Display.block flex(1.0, 1.0, FlexBasis.auto)
bottom = 0.px
} }
ref = elementRef ref = elementRef
} }

View File

@ -1,6 +1,7 @@
package ringui package ringui
import react.RBuilder import react.RBuilder
import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
@ -27,8 +28,14 @@ public object AlertTypes {
public var LOADING: String = "loading" public var LOADING: String = "loading"
} }
@JsModule("@jetbrains/ring-ui/components/alert/alert")
internal external object AlertModule {
@JsName("default")
val Alert: RClass<AlertProps>
}
public fun RBuilder.ringAlert(handler: RHandler<AlertProps>) { public fun RBuilder.ringAlert(handler: RHandler<AlertProps>) {
RingUI.Alert { AlertModule.Alert {
handler() handler()
} }
} }

View File

@ -2,6 +2,7 @@ package ringui
import org.w3c.dom.events.MouseEvent import org.w3c.dom.events.MouseEvent
import react.RBuilder import react.RBuilder
import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
@ -28,8 +29,15 @@ public external interface ButtonProps : WithClassName {
public var onMouseDown: (MouseEvent) -> Unit public var onMouseDown: (MouseEvent) -> Unit
} }
@JsModule("@jetbrains/ring-ui/components/button/button")
internal external object ButtonModule {
@JsName("default")
val Button: RClass<ButtonProps>
}
public fun RBuilder.ringButton(handler: RHandler<ButtonProps>) { public fun RBuilder.ringButton(handler: RHandler<ButtonProps>) {
RingUI.Button { ButtonModule.Button {
handler() handler()
} }
} }

View File

@ -1,6 +1,7 @@
package ringui package ringui
import react.RBuilder import react.RBuilder
import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
@ -20,8 +21,14 @@ public external interface DialogProps : WithClassName {
public var autoFocusFirst: Boolean public var autoFocusFirst: Boolean
} }
@JsModule("@jetbrains/ring-ui/components/dialog/dialog")
internal external object DialogModule {
@JsName("default")
val Dialog: RClass<DialogProps>
}
public fun RBuilder.ringDialog(show: Boolean, handler: RHandler<DialogProps>) { public fun RBuilder.ringDialog(show: Boolean, handler: RHandler<DialogProps>) {
RingUI.Dialog { DialogModule.Dialog {
attrs.show = show attrs.show = show
handler() handler()
} }

View File

@ -0,0 +1,46 @@
package ringui
import org.w3c.dom.events.Event
import react.RBuilder
import react.RClass
import react.RHandler
import react.ReactElement
import react.dom.WithClassName
public external interface AnchorProps : WithClassName
public external interface DropdownProps : WithClassName {
/**
* Can be string, React element, or a function accepting an object with {active, pinned} properties and returning a React element
* React element should render some interactive HTML element like `button` or `a`
*/
public var anchor: dynamic //: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
public var initShown: Boolean
public var activeClassName: String
public var clickMode: Boolean
public var hoverMode: Boolean
public var hoverShowTimeOut: Number
public var hoverHideTimeOut: Number
public var onShow: () -> Unit
public var onHide: () -> Unit
public var onMouseEnter: (Event) -> Unit
public var onMouseLeave: (Event) -> Unit
//'data-test': PropTypes.string
}
@JsModule("@jetbrains/ring-ui/components/dropdown/dropdown")
public external object DropdownModule {
public val Anchor: RClass<AnchorProps>
@JsName("default")
public val Dropdown: RClass<DropdownProps>
}
public fun RBuilder.ringDropdown(anchor: dynamic, handler: RHandler<DropdownProps>): ReactElement =
DropdownModule.Dropdown {
attrs {
this.anchor = anchor
}
handler()
}

View File

@ -1,6 +1,7 @@
package ringui package ringui
import react.RBuilder import react.RBuilder
import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
@ -14,8 +15,14 @@ public external interface IconProps : WithClassName {
public var loading: Boolean public var loading: Boolean
} }
@JsModule("@jetbrains/ring-ui/components/icon/icon")
internal external object IconModule {
@JsName("default")
val Icon: RClass<IconProps>
}
public fun RBuilder.ringIcon(handler: RHandler<IconProps>) { public fun RBuilder.ringIcon(handler: RHandler<IconProps>) {
RingUI.Icon { IconModule.Icon {
handler() handler()
} }
} }

View File

@ -2,6 +2,7 @@ package ringui
import org.w3c.dom.events.MouseEvent import org.w3c.dom.events.MouseEvent
import react.RBuilder import react.RBuilder
import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
@ -17,8 +18,14 @@ public external interface LinkProps : WithClassName {
public var onClick: (MouseEvent) -> Unit public var onClick: (MouseEvent) -> Unit
} }
@JsModule("@jetbrains/ring-ui/components/link/link")
internal external object LinkModule {
@JsName("default")
val Link: RClass<LinkProps>
}
public fun RBuilder.ringLink(handler: RHandler<LinkProps>) { public fun RBuilder.ringLink(handler: RHandler<LinkProps>) {
RingUI.Link { LinkModule.Link {
handler() handler()
} }
} }

View File

@ -1,14 +0,0 @@
package ringui
import react.RClass
import ringui.header.HeaderProps
@JsModule("@jetbrains/ring-ui")
public external object RingUI {
public val Alert: RClass<AlertProps>
public val Button: RClass<ButtonProps>
public val Dialog: RClass<DialogProps>
public val Header: RClass<HeaderProps>
public val Link: RClass<LinkProps>
public val Icon: RClass<IconProps>
}

View File

@ -4,10 +4,11 @@ import react.RBuilder
import react.RClass import react.RClass
import react.RHandler import react.RHandler
import react.dom.WithClassName import react.dom.WithClassName
import ringui.RingUI
@JsModule("@jetbrains/ring-ui/components/header/header") @JsModule("@jetbrains/ring-ui/components/header/header")
internal external object HeaderModule { internal external object HeaderModule {
@JsName("default")
val Header: RClass<HeaderProps>
val RerenderableHeader: RClass<HeaderProps> val RerenderableHeader: RClass<HeaderProps>
val Logo: RClass<HeaderLogoProps> val Logo: RClass<HeaderLogoProps>
val Tray: RClass<HeaderTrayProps> val Tray: RClass<HeaderTrayProps>
@ -24,8 +25,9 @@ public external interface HeaderProps : WithClassName {
public var theme: String public var theme: String
} }
public fun RBuilder.ringHeader(handler: RHandler<HeaderProps>) { public fun RBuilder.ringHeader(handler: RHandler<HeaderProps>) {
RingUI.Header { HeaderModule.Header {
handler() handler()
} }
} }

View File

@ -2,14 +2,21 @@ package space.kscience.visionforge.ring
import kotlinx.css.* import kotlinx.css.*
import react.* import react.*
import ringui.grid.RowPosition import react.dom.div
import ringui.grid.ringCol import react.dom.span
import ringui.grid.ringGrid import ringui.ringLink
import ringui.grid.ringRow
import ringui.tabs.ringTab
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.isEmpty
import space.kscience.dataforge.names.length
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.allProperties
import space.kscience.visionforge.ownProperties
import space.kscience.visionforge.react.ThreeCanvasComponent import space.kscience.visionforge.react.ThreeCanvasComponent
import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.Solid import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import styled.css import styled.css
@ -22,42 +29,82 @@ public external interface ThreeCanvasWithControlsProps : RProps {
public var additionalTabs: Map<String, RBuilder.() -> Unit>? public var additionalTabs: Map<String, RBuilder.() -> Unit>?
} }
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.()->Unit){ public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
additionalTabs = (additionalTabs?: emptyMap()) + (title to block) additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
}
public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement = styledDiv {
div {
ringLink {
attrs {
onClick = {
link(Name.EMPTY)
}
}
+"\u2302"
}
if (name != null) {
val tokens = ArrayList<NameToken>(name.length)
name.tokens.forEach { token ->
tokens.add(token)
val fullName = Name(tokens.toList())
span { +"/" }
ringLink {
+token.toString()
attrs {
onClick = {
console.log("Selected = $fullName")
link(fullName)
}
}
}
}
}
}
} }
@JsExport @JsExport
public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dynamic = public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsProps> =
functionalComponent("ThreeViewWithControls") { props -> functionalComponent("ThreeViewWithControls") { props ->
var selected by useState { props.selected } var selected by useState { props.selected }
val onSelect: (Name?) -> Unit = { val onSelect: (Name?) -> Unit = {
selected = it selected = it
} }
val options = useMemo {
val options = useMemo(props.context) {
Canvas3DOptions.invoke { Canvas3DOptions.invoke {
this.onSelect = onSelect this.onSelect = onSelect
} }
} }
styledDiv { val selectedVision = useMemo(selected) {
selected?.let {
when {
it.isEmpty() -> props.solid
else -> (props.solid as? VisionGroup)?.get(it)
}
}
}
flexRow {
css { css {
height = 100.pct flex(1.0, 1.0, FlexBasis.auto)
width = 100.pct flexWrap = FlexWrap.wrap
maxHeight = 100.vh alignItems = Align.stretch
maxWidth = 100.vw alignContent = Align.stretch
} }
ringGrid {
ringRow { flexColumn {
attrs { css {
start = RowPosition.sm minWidth = 600.px
} flex(10.0, 1.0, FlexBasis("600px"))
ringCol { position = Position.relative
attrs {
xs = 12
sm = 12
md = 8
lg = 9
} }
child(ThreeCanvasComponent) { child(ThreeCanvasComponent) {
attrs { attrs {
this.context = props.context this.context = props.context
@ -66,28 +113,59 @@ public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dyn
this.options = options this.options = options
} }
} }
}
ringCol { selectedVision?.let { vision ->
attrs { styledDiv {
xs = 12 css {
sm = 12 position = Position.absolute
md = 4 top = 10.px
lg = 3 right = 10.px
} }
styledDiv { styledDiv {
css { css {
padding(top = 4.px) minWidth = 450.px
width = 100.pct backgroundColor = Color.white
borderRadius = 3.px
borderColor = Color.blue
borderWidth = 1.px
borderStyle = BorderStyle.solid
padding(3.px)
}
propertyEditor(
ownProperties = vision.ownProperties,
allProperties = vision.allProperties(),
updateFlow = vision.propertyChanges,
descriptor = vision.descriptor,
key = selected
)
}
}
styledDiv {
css {
position = Position.absolute
bottom = 10.px
left = 10.px
backgroundColor = Color.white
borderRadius = 3.px
borderColor = Color.blue
borderWidth = 1.px
borderStyle = BorderStyle.solid
padding(3.px)
}
nameCrumbs(selected) { selected = it }
}
}
}
flexColumn {
css {
padding(4.px)
minWidth = 400.px
flex(1.0, 10.0, FlexBasis("300px"))
alignItems = Align.stretch
alignContent = Align.stretch
//border(1.px, BorderStyle.solid, Color.lightGray) //border(1.px, BorderStyle.solid, Color.lightGray)
} }
ringThreeControls(options, props.solid, selected, onSelect) { ringThreeControls(options, props.solid, selected, onSelect, additionalTabs = props.additionalTabs)
props.additionalTabs?.forEach { (title, builder) ->
ringTab(title, title, builder)
}
}
}
}
}
} }
} }
} }

View File

@ -4,7 +4,6 @@ import org.w3c.dom.Element
import react.RBuilder import react.RBuilder
import react.dom.p import react.dom.p
import react.dom.render import react.dom.render
import react.useMemo
import ringui.island.ringIsland import ringui.island.ringIsland
import ringui.island.ringIslandContent import ringui.island.ringIslandContent
import ringui.island.ringIslandHeader import ringui.island.ringIslandHeader
@ -22,14 +21,12 @@ public fun RBuilder.ringPropertyEditor(
descriptor: NodeDescriptor? = vision.descriptor, descriptor: NodeDescriptor? = vision.descriptor,
key: Any? = null, key: Any? = null,
) { ) {
val styles = if (vision is SolidReference) {
val styles = useMemo(vision, key) {
if (vision is SolidReference) {
(vision.styles + vision.prototype.styles).distinct() (vision.styles + vision.prototype.styles).distinct()
} else { } else {
vision.styles vision.styles
} }
}
flexColumn { flexColumn {
ringIsland("Properties") { ringIsland("Properties") {
propertyEditor( propertyEditor(
@ -52,7 +49,7 @@ public fun RBuilder.ringPropertyEditor(
ringIslandContent { ringIslandContent {
if (styles.size == 1) { if (styles.size == 1) {
val styleName = styles.first() val styleName = styles.first()
p{ p {
+styleName +styleName
} }
val style = vision.getStyle(styleName) val style = vision.getStyle(styleName)

View File

@ -1,10 +1,7 @@
package space.kscience.visionforge.ring package space.kscience.visionforge.ring
import kotlinx.css.BorderStyle import kotlinx.css.*
import kotlinx.css.Color
import kotlinx.css.padding
import kotlinx.css.properties.border import kotlinx.css.properties.border
import kotlinx.css.px
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
import org.w3c.files.Blob import org.w3c.files.Blob
@ -17,9 +14,7 @@ import ringui.tabs.ringSmartTabs
import ringui.tabs.ringTab import ringui.tabs.ringTab
import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.meta.withDefault
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.isEmpty
import space.kscience.visionforge.Vision import space.kscience.visionforge.Vision
import space.kscience.visionforge.VisionGroup
import space.kscience.visionforge.encodeToString import space.kscience.visionforge.encodeToString
import space.kscience.visionforge.react.flexColumn import space.kscience.visionforge.react.flexColumn
import space.kscience.visionforge.react.flexRow import space.kscience.visionforge.react.flexRow
@ -27,6 +22,7 @@ import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.react.visionTree import space.kscience.visionforge.react.visionTree
import space.kscience.visionforge.solid.specifications.Canvas3DOptions import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import styled.css import styled.css
import styled.styledDiv
internal fun saveData(event: Event, fileName: String, mimeType: String = "text/plain", dataBuilder: () -> String) { internal fun saveData(event: Event, fileName: String, mimeType: String = "text/plain", dataBuilder: () -> String) {
event.stopPropagation(); event.stopPropagation();
@ -89,38 +85,35 @@ public external interface ThreeControlsProps : RProps {
public var vision: Vision? public var vision: Vision?
public var selected: Name? public var selected: Name?
public var onSelect: (Name?) -> Unit public var onSelect: (Name?) -> Unit
public var additionalTabs: Map<String, RBuilder.() -> Unit>
} }
@JsExport @JsExport
public val ThreeControls: FunctionalComponent<ThreeControlsProps> = functionalComponent { props -> public val ThreeControls: FunctionalComponent<ThreeControlsProps> = functionalComponent { props ->
ringSmartTabs(if (props.selected != null) "Properties" else "Tree") { ringSmartTabs("Tree") {
ringTab("Canvas") {
ringIsland("Canvas configuration") {
canvasControls(props.canvasOptions, props.vision)
}
}
props.vision?.let { props.vision?.let {
ringTab("Tree") { ringTab("Tree") {
styledDiv {
css {
height = 100.pct
overflowY = Overflow.auto
}
ringIsland("Vision tree") { ringIsland("Vision tree") {
visionTree(it, props.selected, props.onSelect) visionTree(it, props.selected, props.onSelect)
} }
} }
} }
if (props.selected != null) {
ringTab("Properties") {
props.selected.let { selected ->
val selectedObject: Vision? = when {
selected == null -> null
selected.isEmpty() -> props.vision
else -> (props.vision as? VisionGroup)?.get(selected)
} }
if (selectedObject != null) { ringTab("Settings") {
ringPropertyEditor(selectedObject, key = selected) ringIsland("Canvas configuration") {
canvasControls(props.canvasOptions, props.vision)
} }
} }
props.additionalTabs.forEach { (name, handler) ->
ringTab(name){
handler()
} }
} }
props.children()
} }
} }
@ -129,13 +122,13 @@ public fun RBuilder.ringThreeControls(
vision: Vision?, vision: Vision?,
selected: Name?, selected: Name?,
onSelect: (Name?) -> Unit = {}, onSelect: (Name?) -> Unit = {},
builder: RBuilder.() -> Unit = {}, additionalTabs: Map<String, RBuilder.() -> Unit>? = null
): ReactElement = child(ThreeControls) { ): ReactElement = child(ThreeControls) {
attrs { attrs {
this.canvasOptions = canvasOptions this.canvasOptions = canvasOptions
this.vision = vision this.vision = vision
this.selected = selected this.selected = selected
this.onSelect = onSelect this.onSelect = onSelect
this.additionalTabs = additionalTabs?: emptyMap()
} }
builder()
} }