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.get
import react.*
import react.dom.h2
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.fetch
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.ring.ThreeCanvasWithControls
import space.kscience.visionforge.ring.tab
import space.kscience.visionforge.root
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.Solids
import styled.styledDiv
external interface GDMLAppProps : RProps {
var context: Context
@ -24,8 +25,8 @@ external interface GDMLAppProps : RProps {
@JsExport
val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
var vision: Solid? by useState { props.vision }
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) {
val parsedVision = when {
@ -43,28 +44,30 @@ val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
vision = parsedVision as? Solid ?: error("Parsed vision is not a solid")
}
styledDiv {
child(ThreeCanvasWithControls) {
attrs {
this.context = props.context
this.solid = vision
this.selected = props.selected
tab("Load") {
fileDrop("(drag file here)") { files ->
val file = files?.get(0)
if (file != null) {
FileReader().apply {
onload = {
val string = result as String
loadData(file.name, string)
}
readAsText(file)
child(ThreeCanvasWithControls) {
attrs {
this.context = props.context
this.solid = vision
this.selected = props.selected
tab("Load") {
h2 {
+"Drag and drop .gdml or .json VisionForge files here"
}
fileDrop("(drag file here)") { files ->
val file = files?.get(0)
if (file != null) {
FileReader().apply {
onload = {
val string = result as String
loadData(file.name, string)
}
readAsText(file)
}
}
}
}
}
}
}

View File

@ -1,28 +1,45 @@
package space.kscience.visionforge.gdml.demo
import kotlinx.browser.document
import kotlinx.css.*
import react.child
import react.dom.render
import space.kscience.dataforge.context.Global
import space.kscience.gdml.GdmlShowCase
import space.kscience.visionforge.Application
import space.kscience.visionforge.bootstrap.useBootstrap
import space.kscience.visionforge.gdml.toVision
import space.kscience.visionforge.solid.three.ThreePlugin
import space.kscience.visionforge.startApplication
import styled.injectGlobal
private class GDMLDemoApp : Application {
override fun start(state: Map<String, Any>) {
useBootstrap()
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
val context = Global.buildContext("demo"){
val context = Global.buildContext("gdml-demo"){
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) {
child(GDMLApp) {
val vision = GdmlShowCase.cubes().toVision()

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package ringui
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@ -27,8 +28,14 @@ public object AlertTypes {
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>) {
RingUI.Alert {
AlertModule.Alert {
handler()
}
}

View File

@ -2,6 +2,7 @@ package ringui
import org.w3c.dom.events.MouseEvent
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@ -28,8 +29,15 @@ public external interface ButtonProps : WithClassName {
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>) {
RingUI.Button {
ButtonModule.Button {
handler()
}
}

View File

@ -1,6 +1,7 @@
package ringui
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@ -20,8 +21,14 @@ public external interface DialogProps : WithClassName {
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>) {
RingUI.Dialog {
DialogModule.Dialog {
attrs.show = show
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
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@ -14,8 +15,14 @@ public external interface IconProps : WithClassName {
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>) {
RingUI.Icon {
IconModule.Icon {
handler()
}
}

View File

@ -2,6 +2,7 @@ package ringui
import org.w3c.dom.events.MouseEvent
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@ -17,8 +18,14 @@ public external interface LinkProps : WithClassName {
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>) {
RingUI.Link {
LinkModule.Link {
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.RHandler
import react.dom.WithClassName
import ringui.RingUI
@JsModule("@jetbrains/ring-ui/components/header/header")
internal external object HeaderModule {
@JsName("default")
val Header: RClass<HeaderProps>
val RerenderableHeader: RClass<HeaderProps>
val Logo: RClass<HeaderLogoProps>
val Tray: RClass<HeaderTrayProps>
@ -24,8 +25,9 @@ public external interface HeaderProps : WithClassName {
public var theme: String
}
public fun RBuilder.ringHeader(handler: RHandler<HeaderProps>) {
RingUI.Header {
HeaderModule.Header {
handler()
}
}

View File

@ -2,14 +2,21 @@ package space.kscience.visionforge.ring
import kotlinx.css.*
import react.*
import ringui.grid.RowPosition
import ringui.grid.ringCol
import ringui.grid.ringGrid
import ringui.grid.ringRow
import ringui.tabs.ringTab
import react.dom.div
import react.dom.span
import ringui.ringLink
import space.kscience.dataforge.context.Context
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.flexColumn
import space.kscience.visionforge.react.flexRow
import space.kscience.visionforge.react.propertyEditor
import space.kscience.visionforge.solid.Solid
import space.kscience.visionforge.solid.specifications.Canvas3DOptions
import styled.css
@ -22,72 +29,143 @@ public external interface ThreeCanvasWithControlsProps : RProps {
public var additionalTabs: Map<String, RBuilder.() -> Unit>?
}
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.()->Unit){
additionalTabs = (additionalTabs?: emptyMap()) + (title to block)
public fun ThreeCanvasWithControlsProps.tab(title: String, block: RBuilder.() -> Unit) {
additionalTabs = (additionalTabs ?: emptyMap()) + (title to block)
}
@JsExport
public val ThreeCanvasWithControls: (props: ThreeCanvasWithControlsProps) -> dynamic =
functionalComponent("ThreeViewWithControls") { props ->
var selected by useState { props.selected }
val onSelect: (Name?) -> Unit = {
selected = it
}
val options = useMemo {
Canvas3DOptions.invoke {
this.onSelect = onSelect
public fun RBuilder.nameCrumbs(name: Name?, link: (Name) -> Unit): ReactElement = styledDiv {
div {
ringLink {
attrs {
onClick = {
link(Name.EMPTY)
}
}
+"\u2302"
}
styledDiv {
css {
height = 100.pct
width = 100.pct
maxHeight = 100.vh
maxWidth = 100.vw
}
ringGrid {
ringRow {
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 {
start = RowPosition.sm
}
ringCol {
attrs {
xs = 12
sm = 12
md = 8
lg = 9
}
child(ThreeCanvasComponent) {
attrs {
this.context = props.context
this.solid = props.solid
this.selected = selected
this.options = options
}
}
}
ringCol {
attrs {
xs = 12
sm = 12
md = 4
lg = 3
}
styledDiv {
css {
padding(top = 4.px)
width = 100.pct
//border(1.px, BorderStyle.solid, Color.lightGray)
}
ringThreeControls(options, props.solid, selected, onSelect) {
props.additionalTabs?.forEach { (title, builder) ->
ringTab(title, title, builder)
}
}
onClick = {
console.log("Selected = $fullName")
link(fullName)
}
}
}
}
}
}
}
@JsExport
public val ThreeCanvasWithControls: FunctionalComponent<ThreeCanvasWithControlsProps> =
functionalComponent("ThreeViewWithControls") { props ->
var selected by useState { props.selected }
val onSelect: (Name?) -> Unit = {
selected = it
}
val options = useMemo(props.context) {
Canvas3DOptions.invoke {
this.onSelect = onSelect
}
}
val selectedVision = useMemo(selected) {
selected?.let {
when {
it.isEmpty() -> props.solid
else -> (props.solid as? VisionGroup)?.get(it)
}
}
}
flexRow {
css {
flex(1.0, 1.0, FlexBasis.auto)
flexWrap = FlexWrap.wrap
alignItems = Align.stretch
alignContent = Align.stretch
}
flexColumn {
css {
minWidth = 600.px
flex(10.0, 1.0, FlexBasis("600px"))
position = Position.relative
}
child(ThreeCanvasComponent) {
attrs {
this.context = props.context
this.solid = props.solid
this.selected = selected
this.options = options
}
}
selectedVision?.let { vision ->
styledDiv {
css {
position = Position.absolute
top = 10.px
right = 10.px
}
styledDiv {
css {
minWidth = 450.px
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)
}
ringThreeControls(options, props.solid, selected, onSelect, additionalTabs = props.additionalTabs)
}
}
}

View File

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

View File

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