A lot of UI refactoring

This commit is contained in:
Alexander Nozik 2020-05-16 19:26:28 +03:00
parent 49b010a387
commit 484423a17a
78 changed files with 1955 additions and 1302 deletions

View File

@ -1,15 +1,11 @@
import scientifik.fx val dataforgeVersion by extra("0.1.8-dev-2")
import scientifik.serialization
val dataforgeVersion by extra("0.1.7")
plugins { plugins {
val toolsVersion = "0.4.2" val toolsVersion = "0.5.0"
id("scientifik.mpp") version toolsVersion apply false id("scientifik.mpp") version toolsVersion apply false
id("scientifik.jvm") version toolsVersion apply false id("scientifik.jvm") version toolsVersion apply false
id("scientifik.js") version toolsVersion apply false id("scientifik.js") version toolsVersion apply false
id("scientifik.publish") version toolsVersion apply false id("scientifik.publish") version toolsVersion apply false
id("org.openjfx.javafxplugin") version "0.0.8" apply false
} }
allprojects { allprojects {
@ -19,12 +15,13 @@ allprojects {
maven("http://maven.jzy3d.org/releases") maven("http://maven.jzy3d.org/releases")
maven("https://kotlin.bintray.com/js-externals") maven("https://kotlin.bintray.com/js-externals")
maven("https://kotlin.bintray.com/kotlin-js-wrappers/") maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
maven("https://dl.bintray.com/mipt-npm/dataforge")
// maven("https://dl.bintray.com/gbaldeck/kotlin") // maven("https://dl.bintray.com/gbaldeck/kotlin")
// maven("https://dl.bintray.com/rjaros/kotlin") // maven("https://dl.bintray.com/rjaros/kotlin")
} }
group = "hep.dataforge" group = "hep.dataforge"
version = "0.1.3-dev" version = "0.1.4-dev"
} }
val githubProject by extra("dataforge-vis") val githubProject by extra("dataforge-vis")
@ -32,9 +29,9 @@ val bintrayRepo by extra("dataforge")
val fxVersion by extra("14") val fxVersion by extra("14")
subprojects { subprojects {
if(name.startsWith("dataforge")) {
apply(plugin = "scientifik.publish") apply(plugin = "scientifik.publish")
serialization()
afterEvaluate {
fx(scientifik.FXModule.CONTROLS, version = fxVersion)
} }
useSerialization()
useFx(FXModule.CONTROLS, version = fxVersion)
} }

View File

@ -32,19 +32,23 @@ kotlin {
dependencies { dependencies {
api("hep.dataforge:dataforge-output-html:$dataforgeVersion") api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
//React, React DOM + Wrappers (chapter 3) api("org.jetbrains:kotlin-react:16.13.1-pre.104-kotlin-1.3.72")
api("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70") api("org.jetbrains:kotlin-react-dom:16.13.1-pre.104-kotlin-1.3.72")
api("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70") api("org.jetbrains.kotlinx:kotlinx-html:0.6.12")
api(npm("react", "16.13.0"))
api(npm("react-dom", "16.13.0"))
//Kotlin Styled (chapter 3) api("org.jetbrains:kotlin-extensions:1.0.1-pre.104-kotlin-1.3.72")
api("org.jetbrains:kotlin-styled:1.0.0-pre.94-kotlin-1.3.70") api("org.jetbrains:kotlin-css-js:1.0.0-pre.94-kotlin-1.3.70")
api(npm("styled-components")) api("org.jetbrains:kotlin-styled:1.0.0-pre.104-kotlin-1.3.72")
api(npm("inline-style-prefixer"))
api(npm("source-map-resolve","0.6.0")) api(npm("core-js", "2.6.5"))
api(npm("file-saver","2.0.2"))
api(npm("react", "16.13.1"))
api(npm("react-dom", "16.13.1"))
api(npm("react-is", "16.13.0"))
api(npm("inline-style-prefixer", "5.1.0"))
api(npm("styled-components", "4.3.2"))
//api(project(":ringui-wrapper"))
} }
} }
} }

View File

@ -83,20 +83,34 @@ abstract class AbstractVisualObject : VisualObject {
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) ?: mergedStyles[name] ?: parent?.getProperty(name, inherit) sequence {
yield(properties?.get(name))
yield(mergedStyles[name])
yield(parent?.getProperty(name, inherit))
}.merge()
} else { } else {
properties?.get(name) ?: mergedStyles[name] sequence {
yield(properties?.get(name))
yield(mergedStyles[name])
}.merge()
} }
} }
/**
* Reset all properties to their default values
*/
fun resetProperties() {
properties?.removeListener(this)
properties = null
}
companion object { companion object {
val descriptor = NodeDescriptor { val descriptor = NodeDescriptor {
defineValue(STYLE_KEY){ value(STYLE_KEY) {
type(ValueType.STRING) type(ValueType.STRING)
multiple = true multiple = true
} }
} }
} }
} }

View File

@ -44,6 +44,9 @@ interface VisualObject : Configurable {
*/ */
fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit fun propertyChanged(name: Name, before: MetaItem<*>?, after: MetaItem<*>?): Unit
/**
* Send a signal that property value should be reevaluated
*/
fun propertyInvalidated(name: Name) = propertyChanged(name, null, null) fun propertyInvalidated(name: Name) = propertyChanged(name, null, null)
/** /**

View File

@ -1,5 +1,8 @@
package hep.dataforge.vis package hep.dataforge.vis
import hep.dataforge.meta.Laminate
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.node
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty import hep.dataforge.names.isEmpty
@ -17,3 +20,15 @@ tailrec fun Name.selectable(): Name? = when {
cutLast().selectable() cutLast().selectable()
} }
} }
fun Sequence<MetaItem<*>?>.merge(): MetaItem<*>?{
return when (val first = filterNotNull().firstOrNull()) {
null -> null
is MetaItem.ValueItem -> first //fast search for first entry if it is value
is MetaItem.NodeItem -> {
//merge nodes if first encountered node is meta
val laminate: Laminate = Laminate(mapNotNull { it.node }.toList())
MetaItem.NodeItem(laminate)
}
}
}

View File

@ -2,22 +2,25 @@ package hep.dataforge.vis
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.ValueDescriptor import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.meta.descriptors.attributes
import hep.dataforge.meta.descriptors.setAttribute
import hep.dataforge.names.toName
import hep.dataforge.values.asValue import hep.dataforge.values.asValue
/** /**
* Extension property to access the "widget" key of [ValueDescriptor] * Extension property to access the "widget" key of [ValueDescriptor]
*/ */
var ValueDescriptor.widget: Meta var ValueDescriptor.widget: Meta
get() = getProperty("widget").node ?: Meta.EMPTY get() = attributes["widget"].node ?: Meta.EMPTY
set(value) { set(value) {
setProperty("widget", value) setAttribute("widget".toName(), value)
} }
/** /**
* Extension property to access the "widget.type" key of [ValueDescriptor] * Extension property to access the "widget.type" key of [ValueDescriptor]
*/ */
var ValueDescriptor.widgetType: String? var ValueDescriptor.widgetType: String?
get() = getProperty("widget.type").string get() = attributes["widget.type"].string
set(value) { set(value) {
setProperty("widget.type", value?.asValue()) setAttribute("widget.type".toName(), value)
} }

View File

@ -12,10 +12,6 @@ inline fun <T : Any> jsObject(builder: T.() -> Unit): T {
inline fun js(builder: dynamic.() -> Unit): dynamic = jsObject(builder) inline fun js(builder: dynamic.() -> Unit): dynamic = jsObject(builder)
//fun <T : Any> clone(obj: T) = objectAssign(jsObject<T> {}, obj)
//inline fun <T : Any> assign(obj: T, builder: T.() -> Unit) = clone(obj).apply(builder)
fun toPlainObjectStripNull(obj: Any) = js { fun toPlainObjectStripNull(obj: Any) = js {
for (key in Object.keys(obj)) { for (key in Object.keys(obj)) {
val value = obj.asDynamic()[key] val value = obj.asDynamic()[key]

View File

@ -1,90 +0,0 @@
package hep.dataforge.vis.editor
import hep.dataforge.meta.Config
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.meta.get
import hep.dataforge.meta.setValue
import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.values.*
import hep.dataforge.vis.widgetType
import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event
import react.RBuilder
import react.dom.*
internal fun RBuilder.valueChooser(root: Config, name: Name, value: Value, descriptor: ValueDescriptor?) {
val onValueChange: (Event) -> Unit = {
val res = when (val t = it.target) {
// (it.target as HTMLInputElement).value
is HTMLInputElement -> if (t.type == "checkbox") {
if (t.checked) True else False
} else {
t.value.asValue()
}
is HTMLSelectElement -> t.value.asValue()
else -> error("Unknown event target: $t")
}
try {
root.setValue(name, res)
} catch (ex: Exception) {
console.error("Can't set config property ${name} to $res")
}
}
div() {
val type = descriptor?.type?.firstOrNull()
when {
type == ValueType.BOOLEAN -> {
input(type = InputType.checkBox) {
attrs {
checked = value.boolean
onChangeFunction = onValueChange
}
}
}
type == ValueType.NUMBER -> input(type = InputType.number, classes = "form-control w-100") {
attrs {
descriptor.attributes["step"].string?.let {
step = it
}
descriptor.attributes["min"].string?.let {
min = it
}
descriptor.attributes["max"].string?.let {
max = it
}
this.defaultValue = value.string
onChangeFunction = onValueChange
}
}
descriptor?.allowedValues?.isNotEmpty() ?: false -> select (classes = "w-100") {
descriptor!!.allowedValues.forEach {
option {
+it.string
}
}
attrs {
multiple = false
onChangeFunction = onValueChange
}
}
descriptor?.widgetType == "color" -> input(type = InputType.color) {
attrs {
this.value = value.string
onChangeFunction = onValueChange
}
}
else -> input(type = InputType.text, classes = "form-control w-100") {
attrs {
this.value = value.string
onChangeFunction = onValueChange
}
}
}
}
}

View File

@ -26,21 +26,21 @@ class MetaEditorDemo : View("Meta editor demo") {
}.asConfig() }.asConfig()
val descriptor = NodeDescriptor { val descriptor = NodeDescriptor {
defineNode("aNode") { node("aNode") {
info = "A root demo node" info = "A root demo node"
defineValue("b") { value("b") {
info = "b number value" info = "b number value"
type(ValueType.NUMBER) type(ValueType.NUMBER)
} }
defineNode("otherNode") { node("otherNode") {
defineValue("otherValue") { value("otherValue") {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(false) default(false)
info = "default value" info = "default value"
} }
} }
} }
defineValue("multiple") { node("multiple") {
info = "A sns value" info = "A sns value"
multiple = true multiple = true
} }

View File

@ -253,9 +253,7 @@ private fun volume(
} }
fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D { fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
val context = GDMLTransformer(this).apply(block) val context = GDMLTransformer(this).apply(block)
return context.finalize(volume(context, world)) return context.finalize(volume(context, world))
} }

View File

@ -1,10 +1,10 @@
import scientifik.serialization import scientifik.*
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
} }
serialization() useSerialization()
kotlin { kotlin {
js { js {
@ -30,9 +30,10 @@ kotlin {
} }
jsMain { jsMain {
dependencies { dependencies {
// api(project(":wrappers")) implementation(project(":ui:bootstrap"))//to be removed later
implementation(npm("three", "0.114.0")) implementation(npm("three", "0.114.0"))
implementation(npm("three-csg-ts", "1.0.1")) implementation(npm("three-csg-ts", "1.0.1"))
api(npm("file-saver", "2.0.2"))
} }
} }
} }

View File

@ -2,6 +2,7 @@ package hep.dataforge.vis.spatial
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.attributes
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
@ -39,7 +40,8 @@ class Material3D : Scheme() {
val MATERIAL_KEY = "material".asName() val MATERIAL_KEY = "material".asName()
internal val COLOR_KEY = "color".asName() internal val COLOR_KEY = "color".asName()
val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY val MATERIAL_COLOR_KEY = MATERIAL_KEY + COLOR_KEY
val SPECULAR_COLOR_KEY = "specularColor".asName() internal val SPECULAR_COLOR_KEY = "specularColor".asName()
val MATERIAL_SPECULAR_COLOR_KEY = MATERIAL_KEY + SPECULAR_COLOR_KEY
internal val OPACITY_KEY = "opacity".asName() internal val OPACITY_KEY = "opacity".asName()
val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY
internal val WIREFRAME_KEY = "wireframe".asName() internal val WIREFRAME_KEY = "wireframe".asName()
@ -48,21 +50,22 @@ class Material3D : Scheme() {
val descriptor by lazy { val descriptor by lazy {
//must be lazy to avoid initialization bug //must be lazy to avoid initialization bug
NodeDescriptor { NodeDescriptor {
defineValue(COLOR_KEY) { value(COLOR_KEY) {
type(ValueType.STRING, ValueType.NUMBER) type(ValueType.STRING, ValueType.NUMBER)
default("#ffffff") default("#ffffff")
widgetType = "color" widgetType = "color"
} }
defineValue(OPACITY_KEY) { value(OPACITY_KEY) {
type(ValueType.NUMBER) type(ValueType.NUMBER)
default(1.0) default(1.0)
config["attributes"] = Meta { attributes {
this["min"] = 0.0 this["min"] = 0.0
this["max"] = 1.0 this["max"] = 1.0
this["step"] = 0.1 this["step"] = 0.1
} }
widgetType = "slider"
} }
defineValue(WIREFRAME_KEY) { value(WIREFRAME_KEY) {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(false) default(false)
} }

View File

@ -50,14 +50,18 @@ class Proxy private constructor(
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) sequence {
?: mergedStyles[name] yield(properties?.get(name))
?: prototype.getProperty(name) yield(mergedStyles[name])
?: parent?.getProperty(name) yield(prototype.getProperty(name))
yield(parent?.getProperty(name, inherit))
}.merge()
} else { } else {
properties?.get(name) sequence {
?: mergedStyles[name] yield(properties?.get(name))
?: prototype.getProperty(name, false) yield(mergedStyles[name])
yield(prototype.getProperty(name, false))
}.merge()
} }
} }
@ -125,14 +129,18 @@ class Proxy private constructor(
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? { override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
return if (inherit) { return if (inherit) {
properties?.get(name) sequence {
?: mergedStyles[name] yield(properties?.get(name))
?: prototype.getProperty(name) yield(mergedStyles[name])
?: parent?.getProperty(name) yield(prototype.getProperty(name))
yield(parent?.getProperty(name, inherit))
}.merge()
} else { } else {
properties?.get(name) sequence {
?: mergedStyles[name] yield(properties?.get(name))
?: prototype.getProperty(name, false) yield(mergedStyles[name])
yield(prototype.getProperty(name, false))
}.merge()
} }
} }

View File

@ -63,18 +63,18 @@ interface VisualObject3D : VisualObject {
val descriptor by lazy { val descriptor by lazy {
NodeDescriptor { NodeDescriptor {
defineValue(VISIBLE_KEY) { value(VISIBLE_KEY) {
type(ValueType.BOOLEAN) type(ValueType.BOOLEAN)
default(true) default(true)
} }
//TODO replace by descriptor merge //TODO replace by descriptor merge
defineValue(VisualObject.STYLE_KEY){ value(VisualObject.STYLE_KEY){
type(ValueType.STRING) type(ValueType.STRING)
multiple = true multiple = true
} }
defineItem(Material3D.MATERIAL_KEY.toString(), Material3D.descriptor) item(Material3D.MATERIAL_KEY.toString(), Material3D.descriptor)
} }
} }
} }

View File

@ -96,7 +96,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
val picked = pick() val picked = pick()
if (picked != null && this.picked != picked) { if (picked != null && this.picked != picked) {
this.picked?.toggleHighlight(false,HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL) this.picked?.toggleHighlight(false, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
picked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL) picked.toggleHighlight(true, HIGHLIGHT_NAME, HIGHLIGHT_MATERIAL)
this.picked = picked this.picked = picked
} }
@ -131,10 +131,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
} }
} }
private fun Object3D.isStatic(): Boolean { //find first non-static parent in this object ancestry
return false
}
private fun Object3D?.upTrace(): Object3D? = if (this?.name?.startsWith("@") == true) parent else this private fun Object3D?.upTrace(): Object3D? = if (this?.name?.startsWith("@") == true) parent else this
private fun pick(): Object3D? { private fun pick(): Object3D? {
@ -144,7 +141,8 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
// calculate objects intersecting the picking ray // calculate objects intersecting the picking ray
return root?.let { root -> return root?.let { root ->
val intersects = raycaster.intersectObject(root, true) val intersects = raycaster.intersectObject(root, true)
val obj = intersects.map { it.`object` }.firstOrNull { !it.isStatic() } //skip invisible objects
val obj = intersects.map { it.`object` }.firstOrNull { it.visible }
obj.upTrace() obj.upTrace()
} }
} }
@ -168,7 +166,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
} }
} }
fun clear(){ fun clear() {
scene.children.find { it.name == "@root" }?.let { scene.children.find { it.name == "@root" }?.let {
scene.remove(it) scene.remove(it)
} }
@ -186,7 +184,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
root = object3D root = object3D
} }
private var highlighted: Object3D? = null private var selected: Object3D? = null
/** /**
* Toggle highlight for the given [Mesh] object * Toggle highlight for the given [Mesh] object
@ -224,15 +222,15 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
*/ */
fun select(name: Name?) { fun select(name: Name?) {
if (name == null) { if (name == null) {
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL) selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
highlighted = null selected = null
return return
} }
val obj = root?.findChild(name) val obj = root?.findChild(name)
if (obj != null && highlighted != obj) { if (obj != null && selected != obj) {
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL) selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
obj.toggleHighlight(true, SELECT_NAME, SELECTED_MATERIAL) obj.toggleHighlight(true, SELECT_NAME, SELECTED_MATERIAL)
highlighted = obj selected = obj
} }
} }

View File

@ -29,7 +29,7 @@ interface ThreeCanvasState : RState {
class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() { class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
var canvas: ThreeCanvas? = null private var canvas: ThreeCanvas? = null
override fun componentDidMount() { override fun componentDidMount() {
if(canvas == null) { if(canvas == null) {

View File

@ -5,6 +5,7 @@ import hep.dataforge.values.ValueType
import hep.dataforge.vis.Colors import hep.dataforge.vis.Colors
import hep.dataforge.vis.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.spatial.Material3D import hep.dataforge.vis.spatial.Material3D
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_SPECULAR_COLOR_KEY
import info.laht.threekt.materials.LineBasicMaterial import info.laht.threekt.materials.LineBasicMaterial
import info.laht.threekt.materials.Material import info.laht.threekt.materials.Material
import info.laht.threekt.materials.MeshBasicMaterial import info.laht.threekt.materials.MeshBasicMaterial

View File

@ -1,8 +1,8 @@
package hep.dataforge.vis.spatial.three package hep.dataforge.vis.spatial.three
import hep.dataforge.js.accordion
import hep.dataforge.js.entry
import hep.dataforge.js.requireJS import hep.dataforge.js.requireJS
import hep.dataforge.vis.bootstrap.accordion
import hep.dataforge.vis.bootstrap.entry
import hep.dataforge.vis.spatial.Visual3D import hep.dataforge.vis.spatial.Visual3D
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import kotlinx.html.* import kotlinx.html.*

View File

@ -1,6 +1,4 @@
import scientifik.DependencyConfiguration import scientifik.*
import scientifik.FXModule
import scientifik.fx
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
@ -8,7 +6,7 @@ plugins {
} }
val fxVersion: String by rootProject.extra val fxVersion: String by rootProject.extra
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION) useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
kotlin { kotlin {
@ -17,6 +15,7 @@ kotlin {
} }
js { js {
useCommonJs()
browser { browser {
webpackTask { webpackTask {
//sourceMaps = false //sourceMaps = false
@ -27,8 +26,14 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api(project(":dataforge-vis-spatial")) implementation(project(":dataforge-vis-spatial"))
api(project(":dataforge-vis-spatial-gdml")) implementation(project(":dataforge-vis-spatial-gdml"))
}
}
jsMain{
dependencies {
implementation(project(":ui:bootstrap"))
implementation(npm("react-file-drop", "3.0.6"))
} }
} }
} }

View File

@ -0,0 +1,40 @@
@file:JsModule("react-file-drop")
@file:JsNonModule
package drop
import org.w3c.dom.DragEvent
import org.w3c.files.FileList
import react.*
external enum class DropEffects {
copy,
move,
link,
none
}
external interface FileDropProps: RProps {
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: RState {
var draggingOverFrame: Boolean
var draggingOverTarget: Boolean
}
external class FileDrop : Component<FileDropProps, FileDropState> {
override fun render(): dynamic
}

View File

@ -0,0 +1,138 @@
package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Context
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.bootstrap.*
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.specifications.Camera
import hep.dataforge.vis.spatial.specifications.Canvas
import hep.dataforge.vis.spatial.three.ThreeCanvas
import hep.dataforge.vis.spatial.three.ThreeCanvasComponent
import hep.dataforge.vis.spatial.three.canvasControls
import org.w3c.files.FileReader
import org.w3c.files.get
import react.RProps
import react.dom.h1
import scientifik.gdml.GDML
import scientifik.gdml.parse
import kotlin.browser.window
import kotlin.math.PI
interface GDMLAppProps : RProps {
var context: Context
var rootObject: VisualObject?
var selected: Name?
}
private val canvasConfig = Canvas {
camera = Camera {
distance = 2100.0
latitude = PI / 6
azimuth = PI + PI / 6
}
}
val GDMLApp = component<GDMLAppProps> { props ->
var selected by state { props.selected }
var canvas: ThreeCanvas? by state { null }
var visual: VisualObject? by state { props.rootObject }
val select: (Name?) -> Unit = {
selected = it
}
fun loadData(name: String, data: String) {
visual = when {
name.endsWith(".gdml") || name.endsWith(".xml") -> {
val gdml = GDML.parse(data)
gdml.toVisual(gdmlConfiguration)
}
name.endsWith(".json") -> VisualGroup3D.parseJson(data)
else -> {
window.alert("File extension is not recognized: $name")
error("File extension is not recognized: $name")
}
}
}
flexColumn {
h1 { +"GDML/JSON loader demo" }
gridRow {
gridColumn(3) {
card("Load data") {
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)
}
}
}
}
//tree
card("Object tree") {
visual?.let {
objectTree(it, selected, select)
}
}
}
gridColumn(6) {
//canvas
(visual as? VisualObject3D)?.let { visual3D ->
child(ThreeCanvasComponent::class) {
attrs {
this.context = props.context
this.obj = visual3D
this.selected = selected
this.clickCallback = select
this.canvasCallback = {
canvas = it
}
}
}
}
}
gridColumn(3) {
gridRow {
//settings
canvas?.let {
card("Canvas configuration") {
canvasControls(it)
}
}
}
gridRow {
namecrumbs(selected, "World") { selected = it }
}
gridRow {
//properties
card("Properties") {
selected.let { selected ->
val selectedObject: VisualObject? = when {
selected == null -> null
selected.isEmpty() -> visual
else -> (visual as? VisualGroup)?.get(selected)
}
if (selectedObject != null) {
configEditor(selectedObject, default = selectedObject.allProperties(), key = selected)
}
}
}
}
}
}
}
}

View File

@ -3,88 +3,17 @@ package hep.dataforge.vis.spatial.gdml.demo
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.js.Application import hep.dataforge.js.Application
import hep.dataforge.js.startApplication import hep.dataforge.js.startApplication
import hep.dataforge.names.Name
import hep.dataforge.names.isEmpty
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.editor.renderObjectTree
import hep.dataforge.vis.editor.visualPropertyEditor
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.VisualObject3D
import hep.dataforge.vis.spatial.gdml.GDMLTransformer import hep.dataforge.vis.spatial.gdml.GDMLTransformer
import hep.dataforge.vis.spatial.gdml.LUnit import hep.dataforge.vis.spatial.gdml.LUnit
import hep.dataforge.vis.spatial.gdml.toVisual import hep.dataforge.vis.spatial.gdml.toVisual
import hep.dataforge.vis.spatial.three.ThreePlugin import react.child
import hep.dataforge.vis.spatial.three.displayCanvasControls import react.dom.div
import hep.dataforge.vis.spatial.three.output import react.dom.render
import org.w3c.dom.*
import org.w3c.files.FileList
import org.w3c.files.FileReader
import org.w3c.files.get
import scientifik.gdml.GDML
import kotlin.browser.document import kotlin.browser.document
import kotlin.browser.window
import kotlin.dom.clear
private class GDMLDemoApp : Application {
/**
* Handle mouse drag according to https://www.html5rocks.com/en/tutorials/file/dndfiles/
*/
private fun handleDragOver(event: DragEvent) {
event.stopPropagation()
event.preventDefault()
event.dataTransfer?.dropEffect = "copy"
}
/** val gdmlConfiguration: GDMLTransformer.() -> Unit = {
* Load data from text file
*/
private fun loadData(event: DragEvent, block: (name: String, data: String) -> Unit) {
event.stopPropagation()
event.preventDefault()
val file = (event.dataTransfer?.files as FileList)[0]
?: throw RuntimeException("Failed to load file")
FileReader().apply {
onload = {
val string = result as String
block(file.name, string)
}
readAsText(file)
}
}
private fun spinner(show: Boolean) {
// if( show){
//
// val style = if (show) {
// "display:block;"
// } else {
// "display:none;"
// }
// document.getElementById("canvas")?.append {
//
// }
}
private fun message(message: String?) {
console.log(message)
// document.getElementById("messages")?.let { element ->
// if (message == null) {
// element.clear()
// } else {
// element.append {
// p {
// +message
// }
// }
// }
// }
}
private val gdmlConfiguration: GDMLTransformer.() -> Unit = {
lUnit = LUnit.CM lUnit = LUnit.CM
volumeAction = { volume -> volumeAction = { volume ->
when { when {
@ -108,88 +37,38 @@ private class GDMLDemoApp : Application {
} }
} }
} }
} }
private class GDMLDemoApp : Application {
override fun start(state: Map<String, Any>) { override fun start(state: Map<String, Any>) {
val context = Global.context("demo") {} val context = Global.context("demo") {}
val three = context.plugins.load(ThreePlugin) val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page") render(element) {
val configElement = document.getElementById("config") ?: error("Element with id 'layers' not found on page") div("h-100") {
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page") child(GDMLApp) {
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page") attrs {
canvasElement.clear() this.context = context
this.rootObject = cubes().toVisual(gdmlConfiguration)
val action: (name: String, data: String) -> Unit = { name, data ->
canvasElement.clear()
spinner(true)
val visual: VisualObject3D = when {
name.endsWith(".gdml") || name.endsWith(".xml") -> {
message("Loading GDML")
val gdml = GDML.format.parse(GDML.serializer(), data)
message("Converting GDML into DF-VIS format")
gdml.toVisual(gdmlConfiguration)
}
name.endsWith(".json") -> VisualGroup3D.parseJson(data)
else -> {
window.alert("File extension is not recognized: $name")
error("File extension is not recognized: $name")
} }
} }
//Optimize tree
//(visual as? VisualGroup3D)?.transformInPlace(UnRef, RemoveSingleChild)
message("Rendering")
//output.camera.layers.enable(1)
val canvas = three.output(canvasElement as HTMLElement)
canvas.camera.layers.set(0)
configElement.displayCanvasControls(canvas)
//tree.visualObjectTree(visual, editor::propertyEditor)
fun selectElement(name: Name) {
val child: VisualObject = when {
name.isEmpty() -> visual
visual is VisualGroup -> visual[name] ?: return
else -> return
}
canvas.select(name)
editorElement.visualPropertyEditor(name, child)
}
// canvas.clickListener = ::selectElement
//tree.visualObjectTree(visual, editor::propertyEditor)
treeElement.renderObjectTree(visual) { treeName ->
selectElement(treeName)
}
canvas.render(visual)
message(null)
spinner(false)
}
(document.getElementById("drop_zone") as? HTMLDivElement)?.apply {
addEventListener("dragover", { handleDragOver(it as DragEvent) }, false)
addEventListener("drop", { loadData(it as DragEvent, action) }, false)
}
(document.getElementById("file_load_button") as? HTMLInputElement)?.apply {
addEventListener("change", {
(it.target as HTMLInputElement).files?.asList()?.first()?.let { file ->
FileReader().apply {
onload = {
val string = result as String
action(file.name, string)
}
readAsText(file)
} }
} }
}, false) // (document.getElementById("file_load_button") as? HTMLInputElement)?.apply {
} // addEventListener("change", {
// (it.target as HTMLInputElement).files?.asList()?.first()?.let { file ->
// FileReader().apply {
// onload = {
// val string = result as String
// action(file.name, string)
// }
// readAsText(file)
// }
// }
// }, false)
// }
} }
} }

View File

@ -0,0 +1,30 @@
package hep.dataforge.vis.spatial.gdml.demo
import drop.FileDrop
import kotlinx.css.*
import kotlinx.css.properties.border
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(style = BorderStyle.dashed, width = 1.px, color = Color.orange)
alignContent = Align.center
}
child(FileDrop::class) {
attrs {
onDrop = { files, _ ->
console.info("loaded $files")
action(files)
}
}
+title
}
}
}

View File

@ -0,0 +1,62 @@
.file-drop {
/* relatively position the container bc the contents are absolute */
position: relative;
height: 100px;
width: 100%;
}
.file-drop > .file-drop-target {
/* basic styles */
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
border-radius: 2px;
/* horizontally and vertically center all content */
display: flex;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
-ms-flex-direction: column;
align-items: center;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
justify-content: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
align-content: center;
-webkit-align-content: center;
-ms-flex-line-pack: center;
text-align: center;
}
.file-drop > .file-drop-target.file-drop-dragging-over-frame {
/* overlay a black mask when dragging over the frame */
border: none;
background-color: rgba(0, 0, 0, 0.65);
box-shadow: none;
z-index: 50;
opacity: 1;
/* typography */
color: white;
}
.file-drop > .file-drop-target.file-drop-dragging-over-target {
/* turn stuff orange when we are dragging over the target */
color: #ff6e40;
box-shadow: 0 0 13px 3px #ff6e40;
}

File diff suppressed because one or more lines are too long

View File

@ -1,52 +0,0 @@
.drop_zone {
outline: 1px solid orange;
}
.loader {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Remove default bullets */
ul, .tree {
list-style-type: none;
}
/* Style the caret/arrow */
.tree-caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
.objTree-label {
cursor: pointer;
}
/* Create the caret/arrow with a unicode, and style it */
.tree-caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
.objTree-leaf::before {
content: "\25C6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.tree-caret-down::before {
transform: rotate(90deg);
}

View File

@ -1,748 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="240"
height="144"
id="svg4136"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="jsoneditor-icons.svg">
<title
id="title6512">JSON Editor Icons</title>
<metadata
id="metadata4148">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>JSON Editor Icons</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs4146" />
<sodipodi:namedview
pagecolor="#ff63ff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1026"
id="namedview4144"
showgrid="true"
inkscape:zoom="4"
inkscape:cx="13.229181"
inkscape:cy="119.82429"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4136"
showguides="false"
borderlayer="false"
inkscape:showpageshadow="true"
showborder="true">
<inkscape:grid
type="xygrid"
id="grid4640"
empspacing="24" />
</sodipodi:namedview>
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1"
height="16"
width="16"
y="4"
x="4" />
<rect
id="svg_1-7"
height="16"
width="16"
y="3.999995"
x="28.000006"
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
x="52.000004"
y="3.999995"
width="16"
height="16"
id="rect4165" />
<rect
id="rect4175"
height="16"
width="16"
y="3.9999852"
x="172.00002"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
id="rect4175-3"
height="16"
width="16"
y="3.999995"
x="196"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<g
id="g4299"
style="stroke:none">
<rect
x="7.0000048"
y="10.999998"
width="9.9999924"
height="1.9999986"
id="svg_1-1"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
x="11.000005"
y="7.0000114"
width="1.9999955"
height="9.9999838"
id="svg_1-1-1"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
</g>
<g
id="g4299-3"
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
style="stroke:none">
<rect
x="7.0000048"
y="10.999998"
width="9.9999924"
height="1.9999986"
id="svg_1-1-0"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
<rect
x="11.000005"
y="7.0000114"
width="1.9999955"
height="9.9999838"
id="svg_1-1-1-9"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
</g>
<rect
id="svg_1-7-5"
height="6.9999905"
width="6.9999909"
y="7.0000048"
x="55.000004"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
x="58"
y="10.00001"
width="6.9999909"
height="6.9999905"
id="rect4354" />
<rect
id="svg_1-7-5-7"
height="6.9999905"
width="6.9999909"
y="10.000005"
x="58.000004"
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
<g
id="g4378">
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="10.999999"
width="7.9999909"
height="1.9999965"
id="svg_1-7-5-3" />
<rect
id="rect4374"
height="1.9999946"
width="11.999995"
y="7.0000005"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
id="rect4376"
height="1.9999995"
width="3.9999928"
y="14.999996"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
</g>
<g
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
id="g4383">
<rect
id="rect4385"
height="1.9999965"
width="7.9999909"
y="10.999999"
x="198"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="7.0000005"
width="11.999995"
height="1.9999946"
id="rect4387" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
x="198"
y="14.999996"
width="3.9999928"
height="1.9999995"
id="rect4389" />
</g>
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-4"
width="16"
height="16"
x="76"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
id="path4351"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
id="path4351-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-25"
width="16"
height="16"
x="100"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
id="path2987"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
id="path2987-1"
inkscape:connector-curvature="0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-73"
width="16"
height="16"
x="124"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
id="path3780"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
id="path3782"
inkscape:connector-curvature="0" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
id="rect3754-35"
width="16"
height="16"
x="148"
y="3.9999199" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
id="path5008-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
id="path5008-2-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<rect
id="svg_1-7-2"
height="1.9999961"
width="11.999996"
y="64"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="svg_1-7-2-2"
height="2.9999905"
width="2.9999907"
y="52"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="52"
width="2.9999907"
height="2.9999905"
id="rect4561" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="80.000008"
y="58"
width="2.9999907"
height="2.9999905"
id="rect4563" />
<rect
id="rect4565"
height="2.9999905"
width="2.9999907"
y="58"
x="85.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
id="rect4567"
height="2.9999905"
width="2.9999907"
y="64"
x="80.000008"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="85.000008"
y="64"
width="2.9999907"
height="2.9999905"
id="rect4569" />
<circle
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4571"
cx="110.06081"
cy="57.939209"
r="4.7438836" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="116.64566"
y="-31.79752"
width="4.229713"
height="6.4053884"
id="rect4563-2"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
<path
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 125,56 138.77027,56.095 132,64 Z"
id="path4613"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615"
d="M 149,64 162.77027,63.905 156,56 Z"
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="54"
y="53"
width="11.999996"
height="1.9999961"
id="rect4638" />
<rect
id="svg_1-7-2-24"
height="1.9999957"
width="12.99999"
y="-56"
x="53"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
transform="matrix(0,1,-1,0,0,0)" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
x="53"
y="-66"
width="12.99999"
height="1.9999957"
id="rect4657" />
<rect
id="rect4659"
height="0.99999291"
width="11.999999"
y="57"
x="54"
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="88.000122"
width="11.999996"
height="1.9999961"
id="rect4661" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="76.000122"
width="2.9999907"
height="2.9999905"
id="rect4663" />
<rect
id="rect4665"
height="2.9999905"
width="2.9999907"
y="76.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
id="rect4667"
height="2.9999905"
width="2.9999907"
y="82.000122"
x="80.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="85.000008"
y="82.000122"
width="2.9999907"
height="2.9999905"
id="rect4669" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="80.000008"
y="88.000122"
width="2.9999907"
height="2.9999905"
id="rect4671" />
<rect
id="rect4673"
height="2.9999905"
width="2.9999907"
y="88.000122"
x="85.000008"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<circle
r="4.7438836"
cy="81.939331"
cx="110.06081"
id="circle4675"
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
id="rect4677"
height="6.4053884"
width="4.229713"
y="-14.826816"
x="133.6163"
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4679"
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
id="path4681"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
id="rect4683"
height="1.9999961"
width="11.999996"
y="77.000122"
x="54"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
<rect
transform="matrix(0,1,-1,0,0,0)"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="77.000122"
y="-56"
width="12.99999"
height="1.9999957"
id="rect4685" />
<rect
id="rect4687"
height="1.9999957"
width="12.99999"
y="-66"
x="77.000122"
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
transform="matrix(0,1,-1,0,0,0)" />
<rect
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
x="54"
y="81.000122"
width="11.999999"
height="0.99999291"
id="rect4689" />
<rect
id="rect4761-1"
height="1.9999945"
width="15.99999"
y="101"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-0"
height="1.9999945"
width="15.99999"
y="105"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-7"
height="1.9999945"
width="9"
y="109"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1"
height="1.9999945"
width="12"
y="125"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4"
height="1.9999945"
width="10"
y="137"
x="76.000008"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4"
height="1.9999945"
width="10"
y="129"
x="82"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<rect
id="rect4761-1-1-4-4-3"
height="1.9999945"
width="9"
y="133"
x="82"
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
id="path3055-0-77" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9850574,108.015 14.0298856,-0.03"
id="path5244-5-0-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.9849874,132.015 14.0298866,-0.03"
id="path5244-5-0-5-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
id="path4138-12" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
id="path4138-1-3" />
<path
id="path6191"
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
id="path6193" />
<path
id="path6195"
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:connector-curvature="0" />
<path
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4500"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073242"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
inkscape:transform-center-x="-1.2779026" />
<path
inkscape:transform-center-x="1.277902"
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073242"
sodipodi:cx="-36.611614"
sodipodi:sides="3"
id="path4502"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="scale(-1,1)" />
<path
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="false"
sodipodi:arg2="1.0471976"
sodipodi:arg1="0"
sodipodi:r2="2.5558052"
sodipodi:r1="5.1116104"
sodipodi:cy="60.073212"
sodipodi:cx="11.55581"
sodipodi:sides="3"
id="path4504"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
sodipodi:type="star"
transform="matrix(0,1,-1,0,72.0074,71.7877)"
inkscape:transform-center-y="1.2779029" />
<path
inkscape:transform-center-y="-1.2779026"
transform="matrix(0,-1,-1,0,96,96)"
sodipodi:type="star"
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
id="path4506"
sodipodi:sides="3"
sodipodi:cx="11.55581"
sodipodi:cy="60.073212"
sodipodi:r1="5.1116104"
sodipodi:r2="2.5558052"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
id="path4615-5"
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,55 0,6 2,0 0,-6"
id="path4300"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 179,62 0,2 2,0 0,-2"
id="path4300-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.8"
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
id="path4268"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<rect
id="rect4175-3-5"
height="16"
width="16"
y="4"
x="220"
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
id="path3546"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<g
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
id="g4383-6">
<rect
id="rect4385-2"
height="1.2499905"
width="5.9999924"
y="12.625005"
x="198.00002"
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="15.125007"
width="7.4999928"
height="1.2499949"
id="rect4387-9" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="7.6250024"
width="2.9999909"
height="1.2499905"
id="rect4389-1-0" />
<rect
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
x="198.00002"
y="10.125004"
width="4.4999919"
height="1.2499905"
id="rect4389-1-9" />
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
id="path4402"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
</g>
<path
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
id="path3546-2-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<rect
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
id="svg_1-3"
height="16"
width="16"
y="28"
x="4" />
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="path4402-5-7"
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</svg>

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -4,45 +4,14 @@
<meta charset="utf-8"> <meta charset="utf-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">--> <!-- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
<title>Three js demo for particle physics</title> <title>Three js demo for particle physics</title>
<script type="text/javascript" src="main.bundle.js"></script> <link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="css/jsoneditor.min.css">
<link rel="stylesheet" href="css/main.css"> <link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/fileDrop.css">
<script type="text/javascript" src="main.bundle.js"></script>
<script type="text/javascript" src ="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src ="js/bootstrap.bundle.min.js"></script>
</head> </head>
<body class="testApp"> <body class="testApp">
<div class="container-fluid" id = "app"> </div>
<div class="container"> </body>
<h1>GDML demo</h1>
</div>
<div class="container-fluid h-100">
<div class="row h-100">
<div class="col-lg-3">
<div class="card">
<div class="card-body">
<a>Load file:</a>
<input type="file" id="file_load_button" accept=".gdml, application/xml, application/json"/>
</div>
</div>
<div class="card">
<div class="card-body" id="drop_zone">
Load data
<br/>
(drag file here)
</div>
</div>
<div class="overflow-auto" id="tree"></div>
</div>
<div class="col-lg-6">
<div id="canvas"></div>
</div>
<div class="col-lg-3">
<div class="row" id="config"></div>
<div class="row" id="editor"></div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script></body>
</html> </html>

View File

@ -5,9 +5,9 @@ import hep.dataforge.vis.editor.VisualObjectEditorFragment
import hep.dataforge.vis.editor.VisualObjectTreeFragment import hep.dataforge.vis.editor.VisualObjectTreeFragment
import hep.dataforge.vis.spatial.Material3D import hep.dataforge.vis.spatial.Material3D
import hep.dataforge.vis.spatial.Visual3D import hep.dataforge.vis.spatial.Visual3D
import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.fx.FX3DPlugin import hep.dataforge.vis.spatial.fx.FX3DPlugin
import hep.dataforge.vis.spatial.fx.FXCanvas3D import hep.dataforge.vis.spatial.fx.FXCanvas3D
import hep.dataforge.vis.spatial.gdml.toVisual
import javafx.geometry.Orientation import javafx.geometry.Orientation
import javafx.scene.Parent import javafx.scene.Parent
import javafx.stage.FileChooser import javafx.stage.FileChooser
@ -36,10 +36,15 @@ class GDMLView : View() {
buttonbar { buttonbar {
button("Load GDML/json") { button("Load GDML/json") {
action { action {
runAsync {
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull() val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
?: return@action ?: return@runAsync null
val visual: VisualGroup3D = Visual3D.readFile(file) Visual3D.readFile(file)
canvas.render(visual) } ui {
if (it != null) {
canvas.render(it)
}
}
} }
} }
} }
@ -51,6 +56,14 @@ class GDMLView : View() {
} }
} }
init {
runAsync {
cubes().toVisual()
} ui {
canvas.render(it)
}
}
companion object { companion object {
private val fileNameFilter = arrayOf( private val fileNameFilter = arrayOf(
FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml"), FileChooser.ExtensionFilter("GDML", "*.gdml", "*.xml"),

View File

@ -1,3 +1,5 @@
import kotlinx.atomicfu.plugin.gradle.withKotlinTargets
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import scientifik.jsDistDirectory import scientifik.jsDistDirectory
plugins { plugins {
@ -13,7 +15,7 @@ kotlin {
val installJS = tasks.getByName("jsBrowserDistribution") val installJS = tasks.getByName("jsBrowserDistribution")
js{ js {
browser { browser {
dceTask { dceTask {
dceOptions { dceOptions {
@ -28,14 +30,15 @@ kotlin {
jvm { jvm {
withJava() withJava()
compilations.findByName("main").apply { compilations[MAIN_COMPILATION_NAME]?.apply {
tasks.getByName<ProcessResources>("jvmProcessResources") { tasks.getByName<ProcessResources>(processResourcesTaskName) {
dependsOn(installJS) dependsOn(installJS)
afterEvaluate { afterEvaluate {
from(project.jsDistDirectory) from(project.jsDistDirectory)
} }
} }
} }
} }
sourceSets { sourceSets {
@ -53,6 +56,7 @@ kotlin {
} }
jsMain { jsMain {
dependencies { dependencies {
implementation(project(":ui:bootstrap"))
implementation("io.ktor:ktor-client-js:$ktorVersion") implementation("io.ktor:ktor-client-js:$ktorVersion")
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion") implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
implementation(npm("text-encoding")) implementation(npm("text-encoding"))
@ -71,16 +75,3 @@ kotlin {
application { application {
mainClassName = "ru.mipt.npm.muon.monitor.server.MMServerKt" mainClassName = "ru.mipt.npm.muon.monitor.server.MMServerKt"
} }
//configure<JavaFXOptions> {
// modules("javafx.controls")
//}
val common = project(":dataforge-vis-common")
val copyJsResourcesFromCommon by tasks.creating(Copy::class){
from(common.buildDir.resolve("processedResources\\js\\main\\"))
into(buildDir.resolve("processedResources\\js\\main\\"))
}
tasks.getByPath("jsProcessResources").dependsOn(copyJsResourcesFromCommon)

View File

@ -62,6 +62,7 @@ class Model {
fun reset() { fun reset() {
map.values.forEach { map.values.forEach {
it.config
it.setProperty(Material3D.MATERIAL_COLOR_KEY, null) it.setProperty(Material3D.MATERIAL_COLOR_KEY, null)
} }
tracks.removeAll() tracks.removeAll()

View File

@ -1,15 +1,15 @@
package ru.mipt.npm.muon.monitor package ru.mipt.npm.muon.monitor
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.js.card
import hep.dataforge.js.component
import hep.dataforge.js.state
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.isEmpty import hep.dataforge.names.isEmpty
import hep.dataforge.vis.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.editor.configEditor import hep.dataforge.vis.bootstrap.card
import hep.dataforge.vis.editor.objectTree import hep.dataforge.vis.bootstrap.configEditor
import hep.dataforge.vis.bootstrap.objectTree
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import hep.dataforge.vis.spatial.specifications.Camera import hep.dataforge.vis.spatial.specifications.Camera
import hep.dataforge.vis.spatial.specifications.Canvas import hep.dataforge.vis.spatial.specifications.Canvas
import hep.dataforge.vis.spatial.three.ThreeCanvas import hep.dataforge.vis.spatial.three.ThreeCanvas
@ -48,13 +48,14 @@ val MMApp = component<MMAppProps> { props ->
} }
val visual = props.model.root val visual = props.model.root
div("row") { div("row") {
h1("mx-auto") { h1("mx-auto") {
+"Muon monitor demo" +"GDML/JSON render demo"
} }
} }
div("row") { div("row") {
div("col-lg-3 mh-100 px-0") { div("col-lg-3 px-0 overflow-auto") {
//tree //tree
card("Object tree") { card("Object tree") {
objectTree(visual, selected, select) objectTree(visual, selected, select)
@ -154,7 +155,7 @@ val MMApp = component<MMAppProps> { props ->
else -> visual[selected] else -> visual[selected]
} }
if (selectedObject != null) { if (selectedObject != null) {
configEditor(selectedObject, default = selectedObject.allProperties()) configEditor(selectedObject, default = selectedObject.allProperties(), key = selected)
} }
} }
} }

View File

@ -9,6 +9,7 @@ import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer import io.ktor.client.features.json.serializer.KotlinxSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import react.child import react.child
import react.dom.div
import react.dom.render import react.dom.render
import kotlin.browser.document import kotlin.browser.document
@ -30,6 +31,7 @@ private class MMDemoApp : Application {
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page") val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
render(element) { render(element) {
div("container-fluid h-100") {
child(MMApp) { child(MMApp) {
attrs { attrs {
model = this@MMDemoApp.model model = this@MMDemoApp.model
@ -39,6 +41,7 @@ private class MMDemoApp : Application {
} }
} }
} }
}
} }
fun main() { fun main() {

View File

@ -1,6 +1,4 @@
import scientifik.DependencyConfiguration import scientifik.*
import scientifik.FXModule
import scientifik.fx
plugins { plugins {
id("scientifik.mpp") id("scientifik.mpp")
@ -8,7 +6,7 @@ plugins {
} }
val fxVersion: String by rootProject.extra val fxVersion: String by rootProject.extra
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION) useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
kotlin { kotlin {

View File

@ -21,6 +21,7 @@ kotlin {
dependencies { dependencies {
api(project(":dataforge-vis-spatial")) api(project(":dataforge-vis-spatial"))
api(project(":dataforge-vis-spatial-gdml")) api(project(":dataforge-vis-spatial-gdml"))
api(project(":ui:bootstrap"))
} }
} }
} }

View File

@ -2,8 +2,8 @@ import hep.dataforge.context.Global
import hep.dataforge.js.Application import hep.dataforge.js.Application
import hep.dataforge.js.startApplication import hep.dataforge.js.startApplication
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.vis.editor.objectTree import hep.dataforge.vis.bootstrap.objectTree
import hep.dataforge.vis.editor.visualPropertyEditor import hep.dataforge.vis.bootstrap.visualPropertyEditor
import hep.dataforge.vis.spatial.Point3D import hep.dataforge.vis.spatial.Point3D
import hep.dataforge.vis.spatial.VisualGroup3D import hep.dataforge.vis.spatial.VisualGroup3D
import hep.dataforge.vis.spatial.box import hep.dataforge.vis.spatial.box
@ -45,8 +45,6 @@ private class PlayGroundApp : Application {
} }
} }
} }
} }
} }

View File

@ -25,6 +25,11 @@ pluginManagement {
rootProject.name = "dataforge-vis" rootProject.name = "dataforge-vis"
include( include(
":ui",
":ui:react",
":ui:ring",
":ui:material",
":ui:bootstrap",
":dataforge-vis-common", ":dataforge-vis-common",
":dataforge-vis-spatial", ":dataforge-vis-spatial",
":dataforge-vis-spatial-gdml", ":dataforge-vis-spatial-gdml",
@ -33,13 +38,3 @@ include(
":demo:muon-monitor", ":demo:muon-monitor",
":playground" ":playground"
) )
//if(file("../dataforge-core").exists()) {
// includeBuild("../dataforge-core"){
// dependencySubstitution {
// //substitute(module("hep.dataforge:dataforge-output")).with(project(":dataforge-output"))
// substitute(module("hep.dataforge:dataforge-output-jvm")).with(project(":dataforge-output"))
// substitute(module("hep.dataforge:dataforge-output-js")).with(project(":dataforge-output"))
// }
// }
//}

View File

@ -0,0 +1,16 @@
plugins {
id("scientifik.js")
}
val dataforgeVersion: String by rootProject.extra
kotlin {
target {
useCommonJs()
}
}
dependencies{
api(project(":dataforge-vis-common"))
api(project(":ui:react"))
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.vis.editor package hep.dataforge.vis.bootstrap
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem

View File

@ -1,15 +1,13 @@
package hep.dataforge.vis.editor package hep.dataforge.vis.bootstrap
import hep.dataforge.js.RFBuilder
import hep.dataforge.js.card
import hep.dataforge.js.component
import hep.dataforge.js.state
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.names.startsWith import hep.dataforge.names.startsWith
import hep.dataforge.vis.VisualGroup import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.isEmpty import hep.dataforge.vis.isEmpty
import hep.dataforge.vis.react.RFBuilder
import hep.dataforge.vis.react.component
import kotlinx.html.classes import kotlinx.html.classes
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element import org.w3c.dom.Element
@ -29,7 +27,7 @@ interface TreeState : RState {
} }
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit { private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
var expanded: Boolean by state{ props.selected?.startsWith(props.name) ?: false } var expanded: Boolean by useState{ props.selected?.startsWith(props.name) ?: false }
val onClick: (Event) -> Unit = { val onClick: (Event) -> Unit = {
expanded = !expanded expanded = !expanded

View File

@ -1,9 +1,13 @@
package hep.dataforge.js package hep.dataforge.vis.bootstrap
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import kotlinx.html.* import kotlinx.html.*
import kotlinx.html.js.div import kotlinx.html.js.div
import kotlinx.html.js.onClickFunction
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import react.RBuilder import react.RBuilder
import react.ReactElement
import react.dom.* import react.dom.*
inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) { inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagConsumer<HTMLElement>.() -> Unit) {
@ -16,7 +20,7 @@ inline fun TagConsumer<HTMLElement>.card(title: String, crossinline block: TagCo
} }
inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit) { inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit) {
div("card w-100 h-100") { div("card w-100") {
div("card-body") { div("card-body") {
h3(classes = "card-title") { h3(classes = "card-title") {
+title +title
@ -26,7 +30,6 @@ inline fun RBuilder.card(title: String, crossinline block: RBuilder.() -> Unit)
} }
} }
fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, DIV.() -> Unit>>) { fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, DIV.() -> Unit>>) {
div("container-fluid") { div("container-fluid") {
div("accordion") { div("accordion") {
@ -59,7 +62,6 @@ fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, D
} }
} }
typealias AccordionBuilder = MutableList<Pair<String, DIV.() -> Unit>> typealias AccordionBuilder = MutableList<Pair<String, DIV.() -> Unit>>
fun AccordionBuilder.entry(title: String, builder: DIV.() -> Unit) { fun AccordionBuilder.entry(title: String, builder: DIV.() -> Unit) {
@ -71,8 +73,8 @@ fun TagConsumer<HTMLElement>.accordion(id: String, builder: AccordionBuilder.()
accordion(id, list) accordion(id, list)
} }
fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) { fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>): ReactElement {
div("container-fluid") { return div("container-fluid") {
div("accordion") { div("accordion") {
attrs { attrs {
this.id = id this.id = id
@ -111,13 +113,102 @@ fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.
} }
} }
fun RBuilder.namecrumbs(name: Name?, rootTitle: String, link: (Name) -> Unit) {
div("container-fluid p-0") {
nav {
attrs {
attributes["aria-label"] = "breadcrumb"
}
ol("breadcrumb") {
li("breadcrumb-item") {
button(classes = "btn btn-link p-0") {
+rootTitle
attrs {
onClickFunction = {
link(Name.EMPTY)
}
}
}
}
if (name != null) {
val tokens = ArrayList<NameToken>(name.length)
name.tokens.forEach { token ->
tokens.add(token)
val fullName = Name(tokens.toList())
li("breadcrumb-item") {
button(classes = "btn btn-link p-0") {
+token.toString()
attrs {
onClickFunction = {
console.log("Selected = $fullName")
link(fullName)
}
}
}
}
}
}
}
}
}
}
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>> typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) { fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
add(title to builder) add(title to builder)
} }
fun RBuilder.accordion(id: String, builder: RAccordionBuilder.() -> Unit) { fun RBuilder.accordion(id: String, builder: RAccordionBuilder.() -> Unit): ReactElement {
val list = ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder) val list = ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
accordion(id, list) return accordion(id, list)
}
fun joinStyles(vararg styles: String?) = styles.joinToString(separator = " ") { it ?: "" }
inline fun RBuilder.flexColumn(classes: String? = null, block: RDOMBuilder<DIV>.() -> Unit) =
div(joinStyles(classes, "d-flex flex-column"), block)
inline fun RBuilder.flexRow(classes: String? = null, block: RDOMBuilder<DIV>.() -> Unit) =
div(joinStyles(classes, "d-flex flex-row"), block)
enum class ContainerSize(val suffix: String) {
DEFAULT(""),
SM("-sm"),
MD("-md"),
LG("-lg"),
XL("-xl"),
FLUID("-fluid")
}
inline fun RBuilder.container(
classes: String? = null,
size: ContainerSize = ContainerSize.FLUID,
block: RDOMBuilder<DIV>.() -> Unit
): ReactElement = div(joinStyles(classes, "container${size.suffix}"), block)
enum class GridMaxSize(val suffix: String) {
NONE(""),
SM("-sm"),
MD("-md"),
LG("-lg"),
XL("-xl")
}
inline fun RBuilder.gridColumn(
weight: Int? = null,
classes: String? = null,
maxSize: GridMaxSize = GridMaxSize.NONE,
block: RDOMBuilder<DIV>.() -> Unit
): ReactElement {
val weightSuffix = weight?.let { "-$it" } ?: ""
return div(joinStyles(classes, "col${maxSize.suffix}$weightSuffix"), block)
}
inline fun RBuilder.gridRow(
classes: String? = null,
block: RDOMBuilder<DIV>.() -> Unit
): ReactElement {
return div(joinStyles(classes, "row"), block)
} }

View File

@ -1,13 +1,14 @@
package hep.dataforge.vis.editor package hep.dataforge.vis.bootstrap
import hep.dataforge.js.RFBuilder
import hep.dataforge.js.component
import hep.dataforge.js.state
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.* import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken import hep.dataforge.names.NameToken
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.Value
import hep.dataforge.vis.react.RFBuilder
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import kotlinx.html.classes import kotlinx.html.classes
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import org.w3c.dom.Element import org.w3c.dom.Element
@ -15,7 +16,7 @@ import org.w3c.dom.events.Event
import react.* import react.*
import react.dom.* import react.dom.*
interface ConfigEditorProps : RProps { interface ConfigEditorItemProps : RProps {
/** /**
* Root config object - always non null * Root config object - always non null
@ -38,19 +39,22 @@ interface ConfigEditorProps : RProps {
var descriptor: NodeDescriptor? var descriptor: NodeDescriptor?
} }
private fun RFBuilder.configEditorItem(props: ConfigEditorProps) { private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = component { props ->
configEditorItem(props)
}
private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) {
var expanded: Boolean by state { true } var expanded: Boolean by state { true }
val item = props.root[props.name] var item: MetaItem<Config>? by state { props.root[props.name] }
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
val defaultItem = props.default?.get(props.name) val defaultItem = props.default?.get(props.name)
val actualItem: MetaItem<Meta>? = item ?: defaultItem ?: descriptorItem?.defaultItem() var actualItem: MetaItem<Meta>? by state { item ?: defaultItem ?: descriptorItem?.defaultItem() }
val token = props.name.last()?.toString() ?: "Properties" val token = props.name.last()?.toString() ?: "Properties"
var kostyl by state { false }
fun update() { fun update() {
kostyl = !kostyl item = props.root[props.name]
actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
} }
useEffectWithCleanup(listOf(props.root)) { useEffectWithCleanup(listOf(props.root)) {
@ -66,6 +70,15 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
expanded = !expanded expanded = !expanded
} }
val valueChanged: (Value?) -> Unit = {
if (it == null) {
props.root.remove(props.name)
} else {
props.root[props.name] = it
}
update()
}
val removeClick: (Event) -> Unit = { val removeClick: (Event) -> Unit = {
props.root.remove(props.name) props.root.remove(props.name)
update() update()
@ -103,7 +116,16 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
keys.forEach { token -> keys.forEach { token ->
li("tree-item align-middle") { li("tree-item align-middle") {
configEditor(props.root, props.name + token, props.descriptor, props.default) child(ConfigEditorItem) {
attrs {
this.key = props.name.toString()
this.root = props.root
this.name = props.name + token
this.default = props.default
this.descriptor = props.descriptor
}
}
//configEditor(props.root, props.name + token, props.descriptor, props.default)
} }
} }
} }
@ -111,8 +133,9 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
} }
is MetaItem.ValueItem -> { is MetaItem.ValueItem -> {
div { div {
div("d-flex flex-row align-items-center") { div("row form-group") {
div("flex-grow-1 p-1 mr-auto tree-label") { div("col d-inline-flex") {
span("tree-label align-self-center") {
+token +token
attrs { attrs {
if (item == null) { if (item == null) {
@ -120,11 +143,17 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
} }
} }
} }
div("d-inline-flex") {
valueChooser(props.root, props.name, actualItem.value, descriptorItem as? ValueDescriptor)
} }
div("d-inline-flex p-1") { div("col-6 d-inline-flex") {
button(classes = "btn btn-link") { valueChooser(
props.name,
actualItem,
descriptorItem as? ValueDescriptor,
valueChanged
)
}
div("col-auto d-inline-flex p-1") {
button(classes = "btn btn-link align-self-center") {
+"\u00D7" +"\u00D7"
attrs { attrs {
if (item == null) { if (item == null) {
@ -141,30 +170,55 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
} }
} }
val ConfigEditor: FunctionalComponent<ConfigEditorProps> = component { configEditorItem(it) } interface ConfigEditorProps : RProps {
var id: Name
var root: Config
var default: Meta?
var descriptor: NodeDescriptor?
}
val ConfigEditor = component<ConfigEditorProps> { props ->
child(ConfigEditorItem) {
attrs {
this.key = ""
this.root = props.root
this.name = Name.EMPTY
this.default = props.default
this.descriptor = props.descriptor
}
}
}
fun RBuilder.configEditor( fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
config: Config, render(this) {
name: Name = Name.EMPTY,
descriptor: NodeDescriptor? = null,
default: Meta? = null
) {
child(ConfigEditor) { child(ConfigEditor) {
attrs { attrs {
this.key = key?.toString() ?: ""
this.root = config
this.descriptor = descriptor
this.default = default
}
}
}
}
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
child(ConfigEditor) {
attrs {
this.key = key?.toString() ?: ""
this.root = config this.root = config
this.name = name
this.descriptor = descriptor this.descriptor = descriptor
this.default = default this.default = default
} }
} }
} }
fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) { fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null, key: Any? = null) {
render(this) { child(ConfigEditor) {
configEditor(config, Name.EMPTY, descriptor, default) attrs {
this.key = key?.toString() ?: ""
this.root = obj.config
this.descriptor = descriptor
this.default = default
}
} }
} }
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
configEditor(obj.config, Name.EMPTY, descriptor ?: obj.descriptor, default)
}

View File

@ -1,6 +1,5 @@
package hep.dataforge.vis.editor package hep.dataforge.vis.bootstrap
import hep.dataforge.js.card
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.names.Name import hep.dataforge.names.Name

View File

@ -0,0 +1,182 @@
package hep.dataforge.vis.bootstrap
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.meta.get
import hep.dataforge.meta.string
import hep.dataforge.meta.value
import hep.dataforge.names.Name
import hep.dataforge.values.*
import hep.dataforge.vis.Colors
import hep.dataforge.vis.widgetType
import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onKeyDownFunction
import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event
import org.w3c.dom.events.KeyboardEvent
import react.*
import react.dom.div
import react.dom.input
import react.dom.option
import react.dom.select
interface ValueChooserProps : RProps {
var item: MetaItem<*>?
var descriptor: ValueDescriptor?
var valueChanged: ((Value?) -> Unit)?
}
interface ValueChooserState : RState {
var value: Value?
var rawInput: Boolean?
}
class ValueChooserComponent(props: ValueChooserProps) : RComponent<ValueChooserProps, ValueChooserState>(props) {
private val element = createRef<HTMLElement>()
override fun ValueChooserState.init(props: ValueChooserProps) {
value = props.item.value
}
private fun getValue(): Value? {
val element = element.current ?: return null//state.element ?: return null
return when (element) {
is HTMLInputElement -> if (element.type == "checkbox") {
if (element.checked) True else False
} else {
element.value.asValue()
}
is HTMLSelectElement -> element.value.asValue()
else -> error("Unknown event target: $element")
}
}
private val valueChanged: (Event) -> Unit = { _ ->
setState {
value = getValue()
}
}
private val valueChangeAndCommit: (Event) -> Unit = { event ->
val res = getValue()
setState {
value = res
}
props.valueChanged?.invoke(res)
}
private val keyDown: (Event) -> Unit = { event ->
if (event is KeyboardEvent && event.key == "Enter") {
props.valueChanged?.invoke(getValue())
}
}
override fun shouldComponentUpdate(
nextProps: ValueChooserProps,
nextState: ValueChooserState
): Boolean = nextProps.item !== props.item
override fun componentDidUpdate(prevProps: ValueChooserProps, prevState: ValueChooserState, snapshot: Any) {
(element.current as? HTMLInputElement)?.let { element ->
if (element.type == "checkbox") {
element.checked = state.value?.boolean ?: false
} else {
element.value = state.value?.string ?: ""
}
element.indeterminate = state.value == null
}
// (state.element as? HTMLSelectElement)?.let { element ->
// state.value?.let { element.value = it.string }
// }
}
private fun RBuilder.stringInput() = input(type = InputType.text) {
attrs {
this.value = state.value?.string ?: ""
onChangeFunction = valueChanged
onKeyDownFunction = keyDown
}
ref = element
}
override fun RBuilder.render() {
div("align-self-center") {
val descriptor = props.descriptor
val type = descriptor?.type?.firstOrNull()
when {
state.rawInput == true -> stringInput()
descriptor?.widgetType == "color" -> input(type = InputType.color) {
ref = element
attrs {
this.value = state.value?.let { value ->
if (value.type == ValueType.NUMBER) Colors.rgbToString(value.int)
else value.string
} ?: "#000000"
onChangeFunction = valueChangeAndCommit
}
}
type == ValueType.BOOLEAN -> {
input(type = InputType.checkBox) {
ref = element
attrs {
checked = state.value?.boolean ?: false
onChangeFunction = valueChangeAndCommit
}
}
}
type == ValueType.NUMBER -> input(type = InputType.number) {
ref = element
attrs {
descriptor.attributes["step"].string?.let {
step = it
}
descriptor.attributes["min"].string?.let {
min = it
}
descriptor.attributes["max"].string?.let {
max = it
}
this.value = state.value?.string ?: ""
onChangeFunction = valueChanged
onKeyDownFunction = keyDown
}
}
descriptor?.allowedValues?.isNotEmpty() ?: false -> select {
descriptor!!.allowedValues.forEach {
option {
+it.string
}
}
ref = element
attrs {
this.value = state.value?.string ?: ""
multiple = false
onChangeFunction = valueChangeAndCommit
}
}
else -> stringInput()
}
}
}
}
internal fun RBuilder.valueChooser(
name: Name,
item: MetaItem<*>?,
descriptor: ValueDescriptor? = null,
callback: (Value?) -> Unit
) {
child(ValueChooserComponent::class) {
attrs {
key = name.toString()
this.item = item
this.descriptor = descriptor
this.valueChanged = callback
}
}
}

0
ui/build.gradle.kts Normal file
View File

View File

@ -0,0 +1,22 @@
plugins {
id("scientifik.js")
}
val dataforgeVersion: String by rootProject.extra
kotlin {
target {
useCommonJs()
}
}
dependencies{
api(project(":dataforge-vis-common"))
api(project(":ui:react"))
api("subroh0508.net.kotlinmaterialui:core:0.3.16")
api("subroh0508.net.kotlinmaterialui:lab:0.3.16")
api(npm("@material-ui/core","4.9.13"))
api(npm("@material-ui/lab","4.0.0-alpha.52"))
//api(npm("@material-ui/icons","4.9.1"))
}

View File

@ -0,0 +1,193 @@
package hep.dataforge.vis.material
import hep.dataforge.meta.*
import hep.dataforge.meta.descriptors.*
import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.isEmpty
import hep.dataforge.names.plus
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import kotlinx.css.Display
import kotlinx.css.display
import kotlinx.css.flexGrow
import kotlinx.css.flexShrink
import kotlinx.html.js.onClickFunction
import materialui.components.button.button
import materialui.components.grid.enums.GridAlignItems
import materialui.components.grid.enums.GridJustify
import materialui.components.grid.grid
import materialui.components.typography.typographyH6
import materialui.lab.components.treeItem.treeItem
import materialui.lab.components.treeView.treeView
import org.w3c.dom.Element
import org.w3c.dom.events.Event
import react.*
import react.dom.render
import react.dom.span
import styled.css
import styled.styledDiv
interface ConfigEditorProps : RProps {
/**
* Root config object - always non null
*/
var root: Config
/**
* Full path to the displayed node in [root]. Could be empty
*/
var name: Name
/**
* Root default
*/
var default: Meta?
/**
* Root descriptor
*/
var descriptor: NodeDescriptor?
}
private fun RBuilder.configEditorItem(
root: Config,
name: Name,
descriptor: NodeDescriptor?,
default: Meta?
) {
val item = root[name]
val descriptorItem: ItemDescriptor? = descriptor?.get(name)
val defaultItem = default?.get(name)
val actualItem: MetaItem<Meta>? = item ?: defaultItem ?: descriptorItem?.defaultItem()
val token = name.last()?.toString() ?: "Properties"
val removeClick: (Event) -> Unit = {
root.remove(name)
}
treeItem {
attrs {
nodeId = name.toString()
label {
row {
attrs {
alignItems = GridAlignItems.stretch
justify = GridJustify.spaceBetween
spacing(1)
}
grid {
typographyH6 {
+token
}
}
if (actualItem is MetaItem.ValueItem) {
styledDiv {
css {
display = Display.flex
flexGrow = 1.0
}
valueChooser(root, name, actualItem.value, descriptorItem as? ValueDescriptor)
}
}
if (!name.isEmpty()) {
styledDiv {
css {
display = Display.flex
flexShrink = 1.0
}
button {
+"\u00D7"
attrs {
if (item == null) {
disabled = true
} else {
onClickFunction = removeClick
}
}
}
}
}
}
}
}
if (actualItem is MetaItem.NodeItem) {
val keys = buildSet<NameToken> {
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
add(NameToken(it))
}
item?.node?.items?.keys?.let { addAll(it) }
defaultItem?.node?.items?.keys?.let { addAll(it) }
}
keys.forEach { token ->
configEditorItem(root, name + token, descriptor, default)
}
}
}
}
val ConfigEditor: FunctionalComponent<ConfigEditorProps> = component { props ->
var kostyl by state { false }
fun update() {
kostyl = !kostyl
}
useEffectWithCleanup(listOf(props.root)) {
props.root.onChange(this) { name, _, _ ->
if (name == props.name) {
update()
}
}
return@useEffectWithCleanup { props.root.removeListener(this) }
}
treeView {
attrs {
defaultCollapseIcon {
span {
+"-"
}
//child(ExpandMoreIcon::class) {}
}//{<ExpandMoreIcon />}
defaultExpandIcon {
span {
+"+"
}
//child(ChevronRightIcon::class) {}
}//{<ChevronRightIcon />}
set("disableSelection", true)
}
configEditorItem(props.root, props.name, props.descriptor, props.default)
}
}
fun RBuilder.configEditor(
config: Config,
name: Name = Name.EMPTY,
descriptor: NodeDescriptor? = null,
default: Meta? = null
) {
child(ConfigEditor) {
attrs {
this.root = config
this.name = name
this.descriptor = descriptor
this.default = default
}
}
}
fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null) {
render(this) {
configEditor(config, Name.EMPTY, descriptor, default)
}
}
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
configEditor(obj.config, Name.EMPTY, descriptor ?: obj.descriptor, default)
}

View File

@ -0,0 +1,16 @@
package hep.dataforge.vis.material
import react.Component
import react.RClass
import react.RProps
import react.RState
//@JsModule("@material-ui/icons/ExpandMore")
//external class ExpandMoreIcon : Component<RProps, RState>{
// override fun render(): dynamic
//}
//
//@JsModule("@material-ui/icons/ChevronRight")
//external class ChevronRightIcon : Component<RProps, RState>{
// override fun render(): dynamic
//}

View File

@ -0,0 +1,98 @@
package hep.dataforge.vis.material
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import kotlinx.html.DIV
import materialui.components.card.card
import materialui.components.cardcontent.cardContent
import materialui.components.cardheader.cardHeader
import materialui.components.container.container
import materialui.components.container.enums.ContainerMaxWidth
import materialui.components.expansionpanel.expansionPanel
import materialui.components.expansionpaneldetails.expansionPanelDetails
import materialui.components.expansionpanelsummary.expansionPanelSummary
import materialui.components.grid.GridElementBuilder
import materialui.components.grid.enums.GridDirection
import materialui.components.grid.enums.GridStyle
import materialui.components.grid.grid
import materialui.components.paper.paper
import materialui.components.typography.typographyH3
import materialui.components.typography.typographyH5
import react.RBuilder
import react.RProps
import react.child
import react.dom.RDOMBuilder
fun accordionComponent(elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) =
component<RProps> {
val expandedIndex: Int? by state { null }
container {
attrs {
maxWidth = ContainerMaxWidth.`false`
}
elements.forEachIndexed { index, (header, body) ->
expansionPanel {
attrs {
expanded = index == expandedIndex
}
expansionPanelSummary {
typographyH5 {
+header
}
}
expansionPanelDetails {
this.body()
}
}
}
}
}
typealias RAccordionBuilder = MutableList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
add(title to builder)
}
fun RBuilder.accordion(builder: RAccordionBuilder.() -> Unit) {
val list: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>> =
ArrayList<Pair<String, RDOMBuilder<DIV>.() -> Unit>>().apply(builder)
child(accordionComponent(list))
}
fun RBuilder.materialCard(title: String, block: RBuilder.() -> Unit) {
card {
cardHeader {
attrs {
this.title = typographyH5 {
+title
}
}
}
cardContent {
paper {
this.block()
}
}
}
}
fun RBuilder.column(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
grid(*classMap) {
attrs {
container = true
direction = GridDirection.column
}
block()
}
fun RBuilder.row(vararg classMap: Pair<GridStyle, String>, block: GridElementBuilder<DIV>.() -> Unit) =
grid(*classMap) {
attrs {
container = true
direction = GridDirection.row
}
block()
}

View File

@ -0,0 +1,92 @@
package hep.dataforge.vis.material
import hep.dataforge.names.Name
import hep.dataforge.names.plus
import hep.dataforge.names.toName
import hep.dataforge.vis.VisualGroup
import hep.dataforge.vis.VisualObject
import hep.dataforge.vis.isEmpty
import hep.dataforge.vis.react.component
import hep.dataforge.vis.react.state
import materialui.lab.components.treeItem.treeItem
import materialui.lab.components.treeView.treeView
import react.FunctionalComponent
import react.RBuilder
import react.RProps
import react.child
import react.dom.span
interface ObjectTreeProps : RProps {
var name: Name
var selected: Name?
var obj: VisualObject
var clickCallback: (Name?) -> Unit
}
private fun RBuilder.treeBranch(name: Name, obj: VisualObject): Unit {
treeItem {
val token = name.last()?.toString() ?: "World"
attrs {
nodeId = name.toString()
label {
span {
+token
}
}
}
if (obj is VisualGroup) {
obj.children.entries
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
.forEach { (childToken, child) ->
treeBranch(name + childToken, child)
}
}
}
}
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
var selected: String? by state { props.selected.toString() }
treeView {
attrs {
this.selected = selected
this.onNodeSelect = { _, selectedItem ->
@Suppress("CAST_NEVER_SUCCEEDS")
selected = (selectedItem as? String)
val itemName = selected?.toName()
props.clickCallback(itemName)
Unit
}
defaultCollapseIcon {
span{
+"-"
}
//child(ExpandMoreIcon::class) {}
}//{<ExpandMoreIcon />}
defaultExpandIcon {
span{
+"+"
}
//child(ChevronRightIcon::class) {}
}//{<ChevronRightIcon />}
}
treeBranch(props.name, props.obj)
}
}
fun RBuilder.objectTree(
visualObject: VisualObject,
selected: Name? = null,
clickCallback: (Name?) -> Unit = {}
) {
child(ObjectTree) {
attrs {
this.name = Name.EMPTY
this.obj = visualObject
this.selected = selected
this.clickCallback = clickCallback
}
}
}

View File

@ -0,0 +1,114 @@
package hep.dataforge.vis.material
import hep.dataforge.meta.Config
import hep.dataforge.meta.descriptors.ValueDescriptor
import hep.dataforge.meta.get
import hep.dataforge.meta.number
import hep.dataforge.meta.setValue
import hep.dataforge.names.Name
import hep.dataforge.values.*
import hep.dataforge.vis.widgetType
import kotlinx.html.InputType
import kotlinx.html.js.onChangeFunction
import kotlinx.html.js.onKeyDownFunction
import materialui.components.input.input
import materialui.components.select.select
import materialui.components.slider.slider
import materialui.components.switches.switch
import materialui.components.textfield.textField
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event
import org.w3c.dom.events.KeyboardEvent
import react.RBuilder
import react.dom.option
internal fun RBuilder.valueChooser(root: Config, name: Name, value: Value, descriptor: ValueDescriptor?) {
val onValueChange: (Event) -> Unit = { event ->
if (event !is KeyboardEvent || event.key == "Enter") {
val res = when (val t = event.target) {
// (it.target as HTMLInputElement).value
is HTMLInputElement -> if (t.type == "checkbox") {
if (t.checked) True else False
} else {
t.value.asValue()
}
is HTMLSelectElement -> t.value.asValue()
else -> error("Unknown event target: $t")
}
try {
root.setValue(name, res)
} catch (ex: Exception) {
console.error("Can't set config property ${name} to $res")
}
}
}
val type = descriptor?.type?.firstOrNull()
when {
descriptor?.widgetType == "slider" -> slider {
attrs {
descriptor.attributes["step"].number?.let {
step = it
}
descriptor.attributes["min"].number?.let {
min = it
}
descriptor.attributes["max"].number?.let {
max = it
}
this.defaultValue = value.number
onChangeFunction = onValueChange
}
}
descriptor?.widgetType == "color" -> input {
attrs {
fullWidth = true
this.type = InputType.color
this.value = value.string
onChangeFunction = onValueChange
}
}
type == ValueType.BOOLEAN -> switch {
attrs {
defaultChecked = value.boolean
onChangeFunction = onValueChange
}
}
type == ValueType.NUMBER -> textField {
attrs {
fullWidth = true
this.type = InputType.number
defaultValue = value.string
onChangeFunction = onValueChange
//onKeyDownFunction = onValueChange
}
}
descriptor?.allowedValues?.isNotEmpty() ?: false -> select {
descriptor!!.allowedValues.forEach {
option {
+it.string
}
}
attrs {
fullWidth = true
multiple = false
onChangeFunction = onValueChange
}
}
else -> textField {
attrs {
this.type = InputType.text
fullWidth = true
this.defaultValue = value.string
//onFocusOutFunction = onValueChange
onKeyDownFunction = onValueChange
}
}
}
}

View File

@ -0,0 +1,37 @@
package materialui.components.slider
import kotlinx.html.DIV
import kotlinx.html.Tag
import materialui.components.MaterialElementBuilder
import materialui.components.getValue
import materialui.components.inputbase.enums.InputBaseStyle
import materialui.components.setValue
import react.RClass
import react.ReactElement
class SliderElementBuilder<Props: SliderProps> internal constructor(
type: RClass<Props>,
classMap: List<Pair<Enum<*>, String>>
) : MaterialElementBuilder<DIV, Props>(type, classMap, { DIV(mapOf(), it) }) {
fun Tag.classes(vararg classMap: Pair<InputBaseStyle, String>) {
classes(classMap.toList())
}
var Tag.defaultValue: Number? by materialProps
var Tag.disabled: Boolean? by materialProps// = false
var Tag.getAriaLabel: String? by materialProps
var Tag.getAriaValueText: String? by materialProps
var Tag.marks: Array<String>? by materialProps
var Tag.max: Number? by materialProps// = 100
var Tag.min: Number? by materialProps// = 0,
var Tag.name: String? by materialProps
var Tag.onChange: ((dynamic, Number) -> Unit)? by materialProps
var Tag.onChangeCommitted: ((dynamic, Number) -> Unit)? by materialProps
var Tag.orientation: SliderOrientation? by materialProps
var Tag.scale: ((Number) -> Number)? by materialProps// {it}
var Tag.step: Number? by materialProps// = 1,
//ThumbComponent = 'span',
var Tag.track: SliderTrack by materialProps
var Tag.value: Number? by materialProps
var Tag.ValueLabelComponent: ReactElement? by materialProps
var Tag.valueLabelDisplay: SliderValueLabelDisplay by materialProps
}

View File

@ -0,0 +1,18 @@
package materialui.components.slider
enum class SliderOrientation {
horizontal,
vertical
}
enum class SliderTrack{
normal,
`false`,
inverted,
}
enum class SliderValueLabelDisplay{
on,
auto,
off
}

View File

@ -0,0 +1,38 @@
package materialui.components.slider
import materialui.components.StandardProps
import materialui.components.input.enums.InputStyle
import react.RBuilder
import react.RClass
import react.ReactElement
@JsModule("@material-ui/core/Slider")
private external val sliderModule: dynamic
external interface SliderProps : StandardProps {
var defaultValue: Number?
var disabled: Boolean?// = false
var getAriaLabel: String?
var getAriaValueText: String?
var marks: Array<String>?
var max: Number?// = 100
var min: Number?// = 0,
var name: String?
var onChange: ((dynamic, Number) -> Unit)?
var onChangeCommitted: ((dynamic, Number) -> Unit)?
var orientation: SliderOrientation?
var scale: ((Number) -> Number)?// {it}
var step: Number? // = 1,
//ThumbComponent = 'span',
var track: SliderTrack
var value: Number?
var ValueLabelComponent: ReactElement?
var valueLabelDisplay: SliderValueLabelDisplay
//valueLabelFormat = Identity,
}
@Suppress("UnsafeCastFromDynamic")
private val sliderComponent: RClass<SliderProps> = sliderModule.default
fun RBuilder.slider(vararg classMap: Pair<InputStyle, String>, block: SliderElementBuilder<SliderProps>.() -> Unit) =
child(SliderElementBuilder(sliderComponent, classMap.toList()).apply(block).create())

30
ui/react/build.gradle.kts Normal file
View File

@ -0,0 +1,30 @@
plugins {
id("scientifik.js")
}
kotlin {
target {
useCommonJs()
}
}
dependencies{
api("org.jetbrains:kotlin-react:16.13.1-pre.104-kotlin-1.3.72")
api("org.jetbrains:kotlin-react-dom:16.13.1-pre.104-kotlin-1.3.72")
api("org.jetbrains.kotlinx:kotlinx-html:0.6.12")
api("org.jetbrains:kotlin-extensions:1.0.1-pre.104-kotlin-1.3.72")
api("org.jetbrains:kotlin-css-js:1.0.0-pre.94-kotlin-1.3.70")
api("org.jetbrains:kotlin-styled:1.0.0-pre.104-kotlin-1.3.72")
api(npm("core-js", "2.6.5"))
api(npm("react", "16.13.1"))
api(npm("react-dom", "16.13.1"))
api(npm("react-is", "16.13.0"))
api(npm("inline-style-prefixer", "5.1.0"))
api(npm("styled-components", "4.3.2"))
}

View File

@ -1,4 +1,4 @@
package hep.dataforge.js package hep.dataforge.vis.react
import react.* import react.*
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty

22
ui/ring/build.gradle.kts Normal file
View File

@ -0,0 +1,22 @@
plugins {
id("scientifik.js")
}
val dataforgeVersion: String by rootProject.extra
kotlin {
target {
useCommonJs()
}
}
dependencies{
api(project(":ui:react"))
implementation(npm("@jetbrains/logos", "1.1.6"))
implementation(npm("@jetbrains/ring-ui", "3.0.13"))
implementation(npm("svg-inline-loader", "0.8.0"))
}

View File

@ -0,0 +1,34 @@
package ringui
import react.RBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/alert/alert.js
external interface AlertProps : WithClassName {
var timeout: Number
var onCloseRequest: () -> Unit
var onClose: () -> Unit
var isShaking: Boolean
var isClosing: Boolean
var inline: Boolean
var showWithAnimation: Boolean
var closeable: Boolean
var type: AlertType
}
typealias AlertType = String
object AlertTypes {
var ERROR = "error"
var MESSAGE = "message"
var SUCCESS = "success"
var WARNING = "warning"
var LOADING = "loading"
}
fun RBuilder.ringAlert(handler: RHandler<AlertProps>) {
RingUI.Alert {
handler()
}
}

View File

@ -0,0 +1,35 @@
package ringui
import org.w3c.dom.events.MouseEvent
import react.RBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/button/button.js
external interface ButtonProps : WithClassName {
var theme: String
var active: Boolean
var danger: Boolean
var delayed: Boolean
var loader: Boolean
var primary: Boolean
var short: Boolean
var text: Boolean
var inline: Boolean
var dropdown: Boolean
var href: String
var icon: dynamic /* string | func */
var iconSize: Number
var iconClassName: String
var onMouseDown: (MouseEvent) -> Unit
}
fun RBuilder.ringButton(handler: RHandler<ButtonProps>) {
RingUI.Button {
handler()
}
}

View File

@ -0,0 +1,28 @@
package ringui
import react.RBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/dialog/dialog.js
external interface DialogProps : WithClassName {
var contentClassName: String
var show: Boolean
var showCloseButton: Boolean
var onOverlayClick: () -> Unit
var onEscPress: () -> Unit
var onCloseClick: () -> Unit
// onCloseAttempt is a common callback for ESC pressing and overlay clicking.
// Use it if you don't need different behaviors for this cases.
var onCloseAttempt: () -> Unit
// focusTrap may break popups inside dialog, so use it carefully
var trapFocus: Boolean
var autoFocusFirst: Boolean
}
fun RBuilder.ringDialog(show: Boolean, handler: RHandler<DialogProps>) {
RingUI.Dialog {
attrs.show = show
handler()
}
}

View File

@ -0,0 +1,21 @@
package ringui
import react.RBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/icon/icon.js
external interface IconProps : WithClassName {
var color: String
var glyph: dynamic /* string | func */
var height: Number
var size: Number
var width: Number
var loading: Boolean
}
fun RBuilder.ringIcon(handler: RHandler<IconProps>) {
RingUI.Icon {
handler()
}
}

View File

@ -0,0 +1,24 @@
package ringui
import org.w3c.dom.events.MouseEvent
import react.RBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/link/link.js
external interface LinkProps : WithClassName {
var innerClassName: String
var active: Boolean
var inherit: Boolean
var pseudo: Boolean
var hover: Boolean
var href: String
var onPlainLeftClick: (MouseEvent) -> Unit
var onClick: (MouseEvent) -> Unit
}
fun RBuilder.ringLink(handler: RHandler<LinkProps>) {
RingUI.Link {
handler()
}
}

View File

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

View File

@ -0,0 +1,38 @@
package ringui
import react.RBuilder
import react.RClass
import react.RHandler
import react.RProps
@JsModule("@jetbrains/ring-ui/components/user-card/user-card")
private external object UserCardModule {
val UserCard: RClass<UserCardProps>
}
// https://github.com/JetBrains/ring-ui/blob/master/components/user-card/card.js
external interface UserCardProps : RProps {
var user: UserCardModel
var wording: UserCardWording
}
data class UserCardModel(
val name: String,
val login: String,
val avatarUrl: String,
val email: String? = null,
val href: String? = null
)
data class UserCardWording(
val banned: String,
val online: String,
val offline: String
)
fun RBuilder.ringUserCard(user: UserCardModel, handler: RHandler<UserCardProps> = {}) {
UserCardModule.UserCard {
attrs.user = user
handler()
}
}

View File

@ -0,0 +1,31 @@
package ringui.header
import ringui.RingUI
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@JsModule("@jetbrains/ring-ui/components/header/header")
internal external object HeaderModule {
val RerenderableHeader: RClass<HeaderProps>
val Logo: RClass<HeaderLogoProps>
val Tray: RClass<HeaderTrayProps>
val TrayIcon: RClass<WithClassName>
val Profile: RClass<WithClassName>
val SmartProfile: RClass<WithClassName>
val Services: RClass<WithClassName>
val SmartServices: RClass<WithClassName>
}
// https://github.com/JetBrains/ring-ui/blob/master/components/header/header.js
external interface HeaderProps : WithClassName {
var spaced: Boolean
var theme: String
}
fun RBuilder.ringHeader(handler: RHandler<HeaderProps>) {
RingUI.Header {
handler()
}
}

View File

@ -0,0 +1,21 @@
package ringui.header
import ringui.IconProps
import kotlinx.html.A
import react.RElementBuilder
import react.RHandler
import styled.StyledDOMBuilder
external interface HeaderLogoProps : IconProps
fun StyledDOMBuilder<A>.ringLogo(handler: RHandler<HeaderLogoProps>) {
HeaderModule.Logo {
handler()
}
}
fun RElementBuilder<HeaderProps>.ringLogo(handler: RHandler<HeaderLogoProps>) {
HeaderModule.Logo {
handler()
}
}

View File

@ -0,0 +1,26 @@
package ringui.header
import ringui.ButtonProps
import react.RElementBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/header/tray.js
external interface HeaderTrayProps : WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/header/tray-icon.js
external interface HeaderTrayIconProps : ButtonProps {
var rotatable: Boolean
}
fun RElementBuilder<HeaderProps>.ringTray(handler: RHandler<HeaderTrayProps>) {
HeaderModule.Tray {
handler()
}
}
fun RElementBuilder<HeaderTrayProps>.ringTrayIcon(handler: RHandler<WithClassName>) {
HeaderModule.TrayIcon {
handler()
}
}

View File

@ -0,0 +1,20 @@
package ringui.island
import react.RElementBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/island/content.js
external interface IslandContentProps : WithClassName {
var scrollableWrapperClassName: String
var fade: Boolean
var bottomBorder: Boolean
var onScroll: () -> Unit
var onScrollToBottom: () -> Unit
}
fun RElementBuilder<IslandProps>.ringIslandContent(handler: RHandler<IslandContentProps>) {
IslandModule.Content {
handler()
}
}

View File

@ -0,0 +1,18 @@
package ringui.island
import react.RElementBuilder
import react.RHandler
import react.dom.WithClassName
// https://github.com/JetBrains/ring-ui/blob/master/components/island/header.js
external interface IslandHeaderProps : WithClassName {
var border: Boolean
var wrapWithTitle: Boolean
var phase: Number
}
fun RElementBuilder<IslandProps>.ringIslandHeader(handler: RHandler<IslandHeaderProps>) {
IslandModule.Header {
handler()
}
}

View File

@ -0,0 +1,32 @@
package ringui.island
import react.RBuilder
import react.RClass
import react.RHandler
import react.dom.WithClassName
@JsModule("@jetbrains/ring-ui/components/island/island")
internal external object IslandModule {
val default: RClass<IslandProps>
val Content: RClass<IslandContentProps>
val Header: RClass<IslandHeaderProps>
val AdaptiveIsland: RClass<IslandProps>
}
// https://github.com/JetBrains/ring-ui/blob/master/components/island/island.js
external interface IslandProps : WithClassName {
val narrow: Boolean
val withoutPaddings: Boolean
}
fun RBuilder.ringIsland(handler: RHandler<IslandProps>) {
IslandModule.default {
handler()
}
}
fun RBuilder.ringAdaptiveIsland(handler: RHandler<IslandProps>) {
IslandModule.AdaptiveIsland {
handler()
}
}

7
ui/ring/webpack.config.d/01.ring.js vendored Normal file
View File

@ -0,0 +1,7 @@
// wrap is useful, because declaring variables in module can be already declared
// module creates own lexical environment
;(function (config) {
const ringConfig = require('@jetbrains/ring-ui/webpack.config').config;
config.module.rules.push(...ringConfig.module.rules);
})(config);

15
ui/ring/webpack.config.d/02.svg.js vendored Normal file
View File

@ -0,0 +1,15 @@
// wrap is useful, because declaring variables in module can be already declared
// module creates own lexical environment
;(function (config) {
const path = require("path");
config.module.rules.push(
{
test: /\.svg$/,
loader: "svg-inline-loader",
options: {removeSVGTagAttrs: false},
include: [
path.resolve(require.resolve("@jetbrains/logos"), "..", "..")
]
}
);
})(config);