A lot of UI refactoring
This commit is contained in:
parent
49b010a387
commit
484423a17a
@ -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)
|
||||||
}
|
}
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
@ -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]
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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.*
|
||||||
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
demo/gdml/src/jsMain/kotlin/drop/FileDrop.kt
Normal file
40
demo/gdml/src/jsMain/kotlin/drop/FileDrop.kt
Normal 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
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
demo/gdml/src/jsMain/resources/css/fileDrop.css
Normal file
62
demo/gdml/src/jsMain/resources/css/fileDrop.css
Normal 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
@ -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);
|
|
||||||
}
|
|
@ -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 |
@ -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>
|
@ -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"),
|
||||||
|
@ -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)
|
|
@ -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()
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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"))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
16
ui/bootstrap/build.gradle.kts
Normal file
16
ui/bootstrap/build.gradle.kts
Normal 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"))
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -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)
|
||||||
}
|
}
|
@ -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)
|
|
||||||
}
|
|
@ -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
|
@ -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
0
ui/build.gradle.kts
Normal file
22
ui/material/build.gradle.kts
Normal file
22
ui/material/build.gradle.kts
Normal 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"))
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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
|
||||||
|
//}
|
@ -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()
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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
30
ui/react/build.gradle.kts
Normal 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"))
|
||||||
|
}
|
@ -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
22
ui/ring/build.gradle.kts
Normal 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"))
|
||||||
|
}
|
34
ui/ring/src/main/kotlin/ringui/Alert.kt
Normal file
34
ui/ring/src/main/kotlin/ringui/Alert.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
35
ui/ring/src/main/kotlin/ringui/Button.kt
Normal file
35
ui/ring/src/main/kotlin/ringui/Button.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
28
ui/ring/src/main/kotlin/ringui/Dialog.kt
Normal file
28
ui/ring/src/main/kotlin/ringui/Dialog.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
21
ui/ring/src/main/kotlin/ringui/Icon.kt
Normal file
21
ui/ring/src/main/kotlin/ringui/Icon.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
24
ui/ring/src/main/kotlin/ringui/Link.kt
Normal file
24
ui/ring/src/main/kotlin/ringui/Link.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
14
ui/ring/src/main/kotlin/ringui/RingUI.kt
Normal file
14
ui/ring/src/main/kotlin/ringui/RingUI.kt
Normal 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>
|
||||||
|
}
|
38
ui/ring/src/main/kotlin/ringui/UserCard.kt
Normal file
38
ui/ring/src/main/kotlin/ringui/UserCard.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
31
ui/ring/src/main/kotlin/ringui/header/Header.kt
Normal file
31
ui/ring/src/main/kotlin/ringui/header/Header.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
21
ui/ring/src/main/kotlin/ringui/header/Logo.kt
Normal file
21
ui/ring/src/main/kotlin/ringui/header/Logo.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
26
ui/ring/src/main/kotlin/ringui/header/Tray.kt
Normal file
26
ui/ring/src/main/kotlin/ringui/header/Tray.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
20
ui/ring/src/main/kotlin/ringui/island/Content.kt
Normal file
20
ui/ring/src/main/kotlin/ringui/island/Content.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
18
ui/ring/src/main/kotlin/ringui/island/Header.kt
Normal file
18
ui/ring/src/main/kotlin/ringui/island/Header.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
32
ui/ring/src/main/kotlin/ringui/island/Island.kt
Normal file
32
ui/ring/src/main/kotlin/ringui/island/Island.kt
Normal 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
7
ui/ring/webpack.config.d/01.ring.js
vendored
Normal 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
15
ui/ring/webpack.config.d/02.svg.js
vendored
Normal 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);
|
Loading…
Reference in New Issue
Block a user