forked from kscience/visionforge
A lot of UI refactoring
This commit is contained in:
parent
49b010a387
commit
484423a17a
@ -1,15 +1,11 @@
|
||||
import scientifik.fx
|
||||
import scientifik.serialization
|
||||
|
||||
val dataforgeVersion by extra("0.1.7")
|
||||
val dataforgeVersion by extra("0.1.8-dev-2")
|
||||
|
||||
plugins {
|
||||
val toolsVersion = "0.4.2"
|
||||
val toolsVersion = "0.5.0"
|
||||
id("scientifik.mpp") version toolsVersion apply false
|
||||
id("scientifik.jvm") version toolsVersion apply false
|
||||
id("scientifik.js") version toolsVersion apply false
|
||||
id("scientifik.publish") version toolsVersion apply false
|
||||
id("org.openjfx.javafxplugin") version "0.0.8" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -19,12 +15,13 @@ allprojects {
|
||||
maven("http://maven.jzy3d.org/releases")
|
||||
maven("https://kotlin.bintray.com/js-externals")
|
||||
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/rjaros/kotlin")
|
||||
}
|
||||
|
||||
group = "hep.dataforge"
|
||||
version = "0.1.3-dev"
|
||||
version = "0.1.4-dev"
|
||||
}
|
||||
|
||||
val githubProject by extra("dataforge-vis")
|
||||
@ -32,9 +29,9 @@ val bintrayRepo by extra("dataforge")
|
||||
val fxVersion by extra("14")
|
||||
|
||||
subprojects {
|
||||
if(name.startsWith("dataforge")) {
|
||||
apply(plugin = "scientifik.publish")
|
||||
serialization()
|
||||
afterEvaluate {
|
||||
fx(scientifik.FXModule.CONTROLS, version = fxVersion)
|
||||
}
|
||||
useSerialization()
|
||||
useFx(FXModule.CONTROLS, version = fxVersion)
|
||||
}
|
@ -32,19 +32,23 @@ kotlin {
|
||||
dependencies {
|
||||
api("hep.dataforge:dataforge-output-html:$dataforgeVersion")
|
||||
|
||||
//React, React DOM + Wrappers (chapter 3)
|
||||
api("org.jetbrains:kotlin-react:16.13.0-pre.94-kotlin-1.3.70")
|
||||
api("org.jetbrains:kotlin-react-dom:16.13.0-pre.94-kotlin-1.3.70")
|
||||
api(npm("react", "16.13.0"))
|
||||
api(npm("react-dom", "16.13.0"))
|
||||
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")
|
||||
|
||||
//Kotlin Styled (chapter 3)
|
||||
api("org.jetbrains:kotlin-styled:1.0.0-pre.94-kotlin-1.3.70")
|
||||
api(npm("styled-components"))
|
||||
api(npm("inline-style-prefixer"))
|
||||
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("source-map-resolve","0.6.0"))
|
||||
api(npm("file-saver","2.0.2"))
|
||||
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"))
|
||||
//api(project(":ringui-wrapper"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,20 +83,34 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
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 {
|
||||
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 {
|
||||
val descriptor = NodeDescriptor {
|
||||
defineValue(STYLE_KEY){
|
||||
value(STYLE_KEY) {
|
||||
type(ValueType.STRING)
|
||||
multiple = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,9 @@ interface VisualObject : Configurable {
|
||||
*/
|
||||
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)
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,8 @@
|
||||
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.isEmpty
|
||||
|
||||
@ -17,3 +20,15 @@ tailrec fun Name.selectable(): Name? = when {
|
||||
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.descriptors.ValueDescriptor
|
||||
import hep.dataforge.meta.descriptors.attributes
|
||||
import hep.dataforge.meta.descriptors.setAttribute
|
||||
import hep.dataforge.names.toName
|
||||
import hep.dataforge.values.asValue
|
||||
|
||||
/**
|
||||
* Extension property to access the "widget" key of [ValueDescriptor]
|
||||
*/
|
||||
var ValueDescriptor.widget: Meta
|
||||
get() = getProperty("widget").node ?: Meta.EMPTY
|
||||
get() = attributes["widget"].node ?: Meta.EMPTY
|
||||
set(value) {
|
||||
setProperty("widget", value)
|
||||
setAttribute("widget".toName(), value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension property to access the "widget.type" key of [ValueDescriptor]
|
||||
*/
|
||||
var ValueDescriptor.widgetType: String?
|
||||
get() = getProperty("widget.type").string
|
||||
get() = attributes["widget.type"].string
|
||||
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)
|
||||
|
||||
//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 {
|
||||
for (key in Object.keys(obj)) {
|
||||
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()
|
||||
|
||||
val descriptor = NodeDescriptor {
|
||||
defineNode("aNode") {
|
||||
node("aNode") {
|
||||
info = "A root demo node"
|
||||
defineValue("b") {
|
||||
value("b") {
|
||||
info = "b number value"
|
||||
type(ValueType.NUMBER)
|
||||
}
|
||||
defineNode("otherNode") {
|
||||
defineValue("otherValue") {
|
||||
node("otherNode") {
|
||||
value("otherValue") {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(false)
|
||||
info = "default value"
|
||||
}
|
||||
}
|
||||
}
|
||||
defineValue("multiple") {
|
||||
node("multiple") {
|
||||
info = "A sns value"
|
||||
multiple = true
|
||||
}
|
||||
|
@ -253,9 +253,7 @@ private fun volume(
|
||||
}
|
||||
|
||||
fun GDML.toVisual(block: GDMLTransformer.() -> Unit = {}): VisualGroup3D {
|
||||
|
||||
val context = GDMLTransformer(this).apply(block)
|
||||
|
||||
return context.finalize(volume(context, world))
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import scientifik.serialization
|
||||
import scientifik.*
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
}
|
||||
|
||||
serialization()
|
||||
useSerialization()
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
@ -30,9 +30,10 @@ kotlin {
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
// api(project(":wrappers"))
|
||||
implementation(project(":ui:bootstrap"))//to be removed later
|
||||
implementation(npm("three", "0.114.0"))
|
||||
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.descriptors.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.attributes
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.names.plus
|
||||
import hep.dataforge.values.ValueType
|
||||
@ -39,7 +40,8 @@ class Material3D : Scheme() {
|
||||
val MATERIAL_KEY = "material".asName()
|
||||
internal val COLOR_KEY = "color".asName()
|
||||
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()
|
||||
val MATERIAL_OPACITY_KEY = MATERIAL_KEY + OPACITY_KEY
|
||||
internal val WIREFRAME_KEY = "wireframe".asName()
|
||||
@ -48,21 +50,22 @@ class Material3D : Scheme() {
|
||||
val descriptor by lazy {
|
||||
//must be lazy to avoid initialization bug
|
||||
NodeDescriptor {
|
||||
defineValue(COLOR_KEY) {
|
||||
value(COLOR_KEY) {
|
||||
type(ValueType.STRING, ValueType.NUMBER)
|
||||
default("#ffffff")
|
||||
widgetType = "color"
|
||||
}
|
||||
defineValue(OPACITY_KEY) {
|
||||
value(OPACITY_KEY) {
|
||||
type(ValueType.NUMBER)
|
||||
default(1.0)
|
||||
config["attributes"] = Meta {
|
||||
attributes {
|
||||
this["min"] = 0.0
|
||||
this["max"] = 1.0
|
||||
this["step"] = 0.1
|
||||
}
|
||||
widgetType = "slider"
|
||||
}
|
||||
defineValue(WIREFRAME_KEY) {
|
||||
value(WIREFRAME_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(false)
|
||||
}
|
||||
|
@ -50,14 +50,18 @@ class Proxy private constructor(
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
properties?.get(name)
|
||||
?: mergedStyles[name]
|
||||
?: prototype.getProperty(name)
|
||||
?: parent?.getProperty(name)
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yield(mergedStyles[name])
|
||||
yield(prototype.getProperty(name))
|
||||
yield(parent?.getProperty(name, inherit))
|
||||
}.merge()
|
||||
} else {
|
||||
properties?.get(name)
|
||||
?: mergedStyles[name]
|
||||
?: prototype.getProperty(name, false)
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
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<*>? {
|
||||
return if (inherit) {
|
||||
properties?.get(name)
|
||||
?: mergedStyles[name]
|
||||
?: prototype.getProperty(name)
|
||||
?: parent?.getProperty(name)
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yield(mergedStyles[name])
|
||||
yield(prototype.getProperty(name))
|
||||
yield(parent?.getProperty(name, inherit))
|
||||
}.merge()
|
||||
} else {
|
||||
properties?.get(name)
|
||||
?: mergedStyles[name]
|
||||
?: prototype.getProperty(name, false)
|
||||
sequence {
|
||||
yield(properties?.get(name))
|
||||
yield(mergedStyles[name])
|
||||
yield(prototype.getProperty(name, false))
|
||||
}.merge()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,18 +63,18 @@ interface VisualObject3D : VisualObject {
|
||||
|
||||
val descriptor by lazy {
|
||||
NodeDescriptor {
|
||||
defineValue(VISIBLE_KEY) {
|
||||
value(VISIBLE_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(true)
|
||||
}
|
||||
|
||||
//TODO replace by descriptor merge
|
||||
defineValue(VisualObject.STYLE_KEY){
|
||||
value(VisualObject.STYLE_KEY){
|
||||
type(ValueType.STRING)
|
||||
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()
|
||||
|
||||
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)
|
||||
this.picked = picked
|
||||
}
|
||||
@ -131,10 +131,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
|
||||
}
|
||||
}
|
||||
|
||||
private fun Object3D.isStatic(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
//find first non-static parent in this object ancestry
|
||||
private fun Object3D?.upTrace(): Object3D? = if (this?.name?.startsWith("@") == true) parent else this
|
||||
|
||||
private fun pick(): Object3D? {
|
||||
@ -144,7 +141,8 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
|
||||
// calculate objects intersecting the picking ray
|
||||
return root?.let { root ->
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -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.remove(it)
|
||||
}
|
||||
@ -186,7 +184,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val canvas: Canv
|
||||
root = object3D
|
||||
}
|
||||
|
||||
private var highlighted: Object3D? = null
|
||||
private var selected: Object3D? = null
|
||||
|
||||
/**
|
||||
* 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?) {
|
||||
if (name == null) {
|
||||
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
|
||||
highlighted = null
|
||||
selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
|
||||
selected = null
|
||||
return
|
||||
}
|
||||
val obj = root?.findChild(name)
|
||||
if (obj != null && highlighted != obj) {
|
||||
highlighted?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
|
||||
if (obj != null && selected != obj) {
|
||||
selected?.toggleHighlight(false, 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>() {
|
||||
|
||||
var canvas: ThreeCanvas? = null
|
||||
private var canvas: ThreeCanvas? = null
|
||||
|
||||
override fun componentDidMount() {
|
||||
if(canvas == null) {
|
||||
|
@ -5,6 +5,7 @@ import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vis.Colors
|
||||
import hep.dataforge.vis.VisualObject
|
||||
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.Material
|
||||
import info.laht.threekt.materials.MeshBasicMaterial
|
||||
|
@ -1,8 +1,8 @@
|
||||
package hep.dataforge.vis.spatial.three
|
||||
|
||||
import hep.dataforge.js.accordion
|
||||
import hep.dataforge.js.entry
|
||||
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.VisualGroup3D
|
||||
import kotlinx.html.*
|
||||
|
@ -1,6 +1,4 @@
|
||||
import scientifik.DependencyConfiguration
|
||||
import scientifik.FXModule
|
||||
import scientifik.fx
|
||||
import scientifik.*
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
@ -8,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
val fxVersion: String by rootProject.extra
|
||||
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
|
||||
kotlin {
|
||||
|
||||
@ -17,6 +15,7 @@ kotlin {
|
||||
}
|
||||
|
||||
js {
|
||||
useCommonJs()
|
||||
browser {
|
||||
webpackTask {
|
||||
//sourceMaps = false
|
||||
@ -27,8 +26,14 @@ kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
api(project(":dataforge-vis-spatial-gdml"))
|
||||
implementation(project(":dataforge-vis-spatial"))
|
||||
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.js.Application
|
||||
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.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.gdml.GDMLTransformer
|
||||
import hep.dataforge.vis.spatial.gdml.LUnit
|
||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
import org.w3c.dom.*
|
||||
import org.w3c.files.FileList
|
||||
import org.w3c.files.FileReader
|
||||
import org.w3c.files.get
|
||||
import scientifik.gdml.GDML
|
||||
import react.child
|
||||
import react.dom.div
|
||||
import react.dom.render
|
||||
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"
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
val gdmlConfiguration: GDMLTransformer.() -> Unit = {
|
||||
lUnit = LUnit.CM
|
||||
volumeAction = { volume ->
|
||||
when {
|
||||
@ -108,88 +37,38 @@ private class GDMLDemoApp : Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GDMLDemoApp : Application {
|
||||
|
||||
override fun start(state: Map<String, Any>) {
|
||||
|
||||
val context = Global.context("demo") {}
|
||||
val three = context.plugins.load(ThreePlugin)
|
||||
//val url = URL("https://drive.google.com/open?id=1w5e7fILMN83JGgB8WANJUYm8OW2s0WVO")
|
||||
val element = document.getElementById("app") ?: error("Element with id 'app' not found on page")
|
||||
|
||||
val canvasElement = document.getElementById("canvas") ?: error("Element with id 'canvas' not found on page")
|
||||
val configElement = document.getElementById("config") ?: error("Element with id 'layers' not found on page")
|
||||
val treeElement = document.getElementById("tree") ?: error("Element with id 'tree' not found on page")
|
||||
val editorElement = document.getElementById("editor") ?: error("Element with id 'editor' not found on page")
|
||||
canvasElement.clear()
|
||||
|
||||
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")
|
||||
render(element) {
|
||||
div("h-100") {
|
||||
child(GDMLApp) {
|
||||
attrs {
|
||||
this.context = context
|
||||
this.rootObject = cubes().toVisual(gdmlConfiguration)
|
||||
}
|
||||
}
|
||||
|
||||
//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 name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">-->
|
||||
<title>Three js demo for particle physics</title>
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
<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/bootstrap.min.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>
|
||||
<body class="testApp">
|
||||
|
||||
<div class="container">
|
||||
<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>
|
||||
<div class="container-fluid" id = "app"> </div>
|
||||
</body>
|
||||
</html>
|
@ -5,9 +5,9 @@ import hep.dataforge.vis.editor.VisualObjectEditorFragment
|
||||
import hep.dataforge.vis.editor.VisualObjectTreeFragment
|
||||
import hep.dataforge.vis.spatial.Material3D
|
||||
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.FXCanvas3D
|
||||
import hep.dataforge.vis.spatial.gdml.toVisual
|
||||
import javafx.geometry.Orientation
|
||||
import javafx.scene.Parent
|
||||
import javafx.stage.FileChooser
|
||||
@ -36,10 +36,15 @@ class GDMLView : View() {
|
||||
buttonbar {
|
||||
button("Load GDML/json") {
|
||||
action {
|
||||
runAsync {
|
||||
val file = chooseFile("Select a GDML/json file", filters = fileNameFilter).firstOrNull()
|
||||
?: return@action
|
||||
val visual: VisualGroup3D = Visual3D.readFile(file)
|
||||
canvas.render(visual)
|
||||
?: return@runAsync null
|
||||
Visual3D.readFile(file)
|
||||
} ui {
|
||||
if (it != null) {
|
||||
canvas.render(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,6 +56,14 @@ class GDMLView : View() {
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
runAsync {
|
||||
cubes().toVisual()
|
||||
} ui {
|
||||
canvas.render(it)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val fileNameFilter = arrayOf(
|
||||
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
|
||||
|
||||
plugins {
|
||||
@ -13,7 +15,7 @@ kotlin {
|
||||
|
||||
val installJS = tasks.getByName("jsBrowserDistribution")
|
||||
|
||||
js{
|
||||
js {
|
||||
browser {
|
||||
dceTask {
|
||||
dceOptions {
|
||||
@ -28,14 +30,15 @@ kotlin {
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
compilations.findByName("main").apply {
|
||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||
compilations[MAIN_COMPILATION_NAME]?.apply {
|
||||
tasks.getByName<ProcessResources>(processResourcesTaskName) {
|
||||
dependsOn(installJS)
|
||||
afterEvaluate {
|
||||
from(project.jsDistDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -53,6 +56,7 @@ kotlin {
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":ui:bootstrap"))
|
||||
implementation("io.ktor:ktor-client-js:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-serialization-js:$ktorVersion")
|
||||
implementation(npm("text-encoding"))
|
||||
@ -71,16 +75,3 @@ kotlin {
|
||||
application {
|
||||
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() {
|
||||
map.values.forEach {
|
||||
it.config
|
||||
it.setProperty(Material3D.MATERIAL_COLOR_KEY, null)
|
||||
}
|
||||
tracks.removeAll()
|
||||
|
@ -1,15 +1,15 @@
|
||||
package ru.mipt.npm.muon.monitor
|
||||
|
||||
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.NameToken
|
||||
import hep.dataforge.names.isEmpty
|
||||
import hep.dataforge.vis.VisualObject
|
||||
import hep.dataforge.vis.editor.configEditor
|
||||
import hep.dataforge.vis.editor.objectTree
|
||||
import hep.dataforge.vis.bootstrap.card
|
||||
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.Canvas
|
||||
import hep.dataforge.vis.spatial.three.ThreeCanvas
|
||||
@ -48,13 +48,14 @@ val MMApp = component<MMAppProps> { props ->
|
||||
}
|
||||
|
||||
val visual = props.model.root
|
||||
|
||||
div("row") {
|
||||
h1("mx-auto") {
|
||||
+"Muon monitor demo"
|
||||
+"GDML/JSON render demo"
|
||||
}
|
||||
}
|
||||
div("row") {
|
||||
div("col-lg-3 mh-100 px-0") {
|
||||
div("col-lg-3 px-0 overflow-auto") {
|
||||
//tree
|
||||
card("Object tree") {
|
||||
objectTree(visual, selected, select)
|
||||
@ -154,7 +155,7 @@ val MMApp = component<MMAppProps> { props ->
|
||||
else -> visual[selected]
|
||||
}
|
||||
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 kotlinx.serialization.json.Json
|
||||
import react.child
|
||||
import react.dom.div
|
||||
import react.dom.render
|
||||
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")
|
||||
|
||||
render(element) {
|
||||
div("container-fluid h-100") {
|
||||
child(MMApp) {
|
||||
attrs {
|
||||
model = this@MMDemoApp.model
|
||||
@ -39,6 +41,7 @@ private class MMDemoApp : Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
|
@ -1,6 +1,4 @@
|
||||
import scientifik.DependencyConfiguration
|
||||
import scientifik.FXModule
|
||||
import scientifik.fx
|
||||
import scientifik.*
|
||||
|
||||
plugins {
|
||||
id("scientifik.mpp")
|
||||
@ -8,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
val fxVersion: String by rootProject.extra
|
||||
fx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
useFx(FXModule.CONTROLS, version = fxVersion, configuration = DependencyConfiguration.IMPLEMENTATION)
|
||||
|
||||
kotlin {
|
||||
|
||||
|
@ -21,6 +21,7 @@ kotlin {
|
||||
dependencies {
|
||||
api(project(":dataforge-vis-spatial"))
|
||||
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.startApplication
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.vis.editor.objectTree
|
||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
||||
import hep.dataforge.vis.bootstrap.objectTree
|
||||
import hep.dataforge.vis.bootstrap.visualPropertyEditor
|
||||
import hep.dataforge.vis.spatial.Point3D
|
||||
import hep.dataforge.vis.spatial.VisualGroup3D
|
||||
import hep.dataforge.vis.spatial.box
|
||||
@ -45,8 +45,6 @@ private class PlayGroundApp : Application {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ pluginManagement {
|
||||
rootProject.name = "dataforge-vis"
|
||||
|
||||
include(
|
||||
":ui",
|
||||
":ui:react",
|
||||
":ui:ring",
|
||||
":ui:material",
|
||||
":ui:bootstrap",
|
||||
":dataforge-vis-common",
|
||||
":dataforge-vis-spatial",
|
||||
":dataforge-vis-spatial-gdml",
|
||||
@ -33,13 +38,3 @@ include(
|
||||
":demo:muon-monitor",
|
||||
":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.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.plus
|
||||
import hep.dataforge.names.startsWith
|
||||
import hep.dataforge.vis.VisualGroup
|
||||
import hep.dataforge.vis.VisualObject
|
||||
import hep.dataforge.vis.isEmpty
|
||||
import hep.dataforge.vis.react.RFBuilder
|
||||
import hep.dataforge.vis.react.component
|
||||
import kotlinx.html.classes
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
@ -29,7 +27,7 @@ interface TreeState : RState {
|
||||
}
|
||||
|
||||
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 = {
|
||||
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.js.div
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.HTMLElement
|
||||
import react.RBuilder
|
||||
import react.ReactElement
|
||||
import react.dom.*
|
||||
|
||||
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) {
|
||||
div("card w-100 h-100") {
|
||||
div("card w-100") {
|
||||
div("card-body") {
|
||||
h3(classes = "card-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>>) {
|
||||
div("container-fluid") {
|
||||
div("accordion") {
|
||||
@ -59,7 +62,6 @@ fun TagConsumer<HTMLElement>.accordion(id: String, elements: List<Pair<String, D
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias AccordionBuilder = MutableList<Pair<String, 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)
|
||||
}
|
||||
|
||||
fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>) {
|
||||
div("container-fluid") {
|
||||
fun RBuilder.accordion(id: String, elements: List<Pair<String, RDOMBuilder<DIV>.() -> Unit>>): ReactElement {
|
||||
return div("container-fluid") {
|
||||
div("accordion") {
|
||||
attrs {
|
||||
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>>
|
||||
|
||||
fun RAccordionBuilder.entry(title: String, builder: RDOMBuilder<DIV>.() -> Unit) {
|
||||
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)
|
||||
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.descriptors.*
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
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.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
@ -15,7 +16,7 @@ import org.w3c.dom.events.Event
|
||||
import react.*
|
||||
import react.dom.*
|
||||
|
||||
interface ConfigEditorProps : RProps {
|
||||
interface ConfigEditorItemProps : RProps {
|
||||
|
||||
/**
|
||||
* Root config object - always non null
|
||||
@ -38,19 +39,22 @@ interface ConfigEditorProps : RProps {
|
||||
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 }
|
||||
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 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"
|
||||
|
||||
var kostyl by state { false }
|
||||
|
||||
fun update() {
|
||||
kostyl = !kostyl
|
||||
item = props.root[props.name]
|
||||
actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||
}
|
||||
|
||||
useEffectWithCleanup(listOf(props.root)) {
|
||||
@ -66,6 +70,15 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
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 = {
|
||||
props.root.remove(props.name)
|
||||
update()
|
||||
@ -103,7 +116,16 @@ private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
|
||||
keys.forEach { token ->
|
||||
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 -> {
|
||||
div {
|
||||
div("d-flex flex-row align-items-center") {
|
||||
div("flex-grow-1 p-1 mr-auto tree-label") {
|
||||
div("row form-group") {
|
||||
div("col d-inline-flex") {
|
||||
span("tree-label align-self-center") {
|
||||
+token
|
||||
attrs {
|
||||
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") {
|
||||
button(classes = "btn btn-link") {
|
||||
div("col-6 d-inline-flex") {
|
||||
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"
|
||||
attrs {
|
||||
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(
|
||||
config: Config,
|
||||
name: Name = Name.EMPTY,
|
||||
descriptor: NodeDescriptor? = null,
|
||||
default: Meta? = null
|
||||
) {
|
||||
fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
|
||||
render(this) {
|
||||
child(ConfigEditor) {
|
||||
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.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, key: Any? = null) {
|
||||
child(ConfigEditor) {
|
||||
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.descriptors.NodeDescriptor
|
||||
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 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