forked from kscience/visionforge
propertyEditor fix (not yet fully functional)
This commit is contained in:
parent
8d3de8688f
commit
3aba8297e8
@ -39,8 +39,10 @@ kotlin {
|
||||
api(npm("styled-components"))
|
||||
api(npm("inline-style-prefixer"))
|
||||
|
||||
|
||||
api(npm("source-map-resolve","0.6.0"))
|
||||
api(npm("bootstrap","4.3.1"))
|
||||
api(npm("popper.js","1.14.7"))
|
||||
api(npm("jquery","3.5.0"))
|
||||
//api(npm("jsoneditor", "8.6.1"))
|
||||
api(npm("file-saver","2.0.2"))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hep.dataforge.vis.editor
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.ItemDescriptor
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.meta.descriptors.defaultItem
|
||||
import hep.dataforge.meta.descriptors.get
|
||||
@ -13,6 +14,7 @@ import kotlinx.html.classes
|
||||
import kotlinx.html.js.onChangeFunction
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.events.Event
|
||||
import react.RBuilder
|
||||
import react.RComponent
|
||||
@ -47,7 +49,7 @@ interface ConfigEditorProps : RProps {
|
||||
class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
|
||||
|
||||
override fun TreeState.init() {
|
||||
expanded = false
|
||||
expanded = true
|
||||
}
|
||||
|
||||
override fun componentDidMount() {
|
||||
@ -70,85 +72,96 @@ class ConfigEditorComponent : RComponent<ConfigEditorProps, TreeState>() {
|
||||
}
|
||||
}
|
||||
|
||||
private val onValueChange: (Event) -> Unit = {
|
||||
val value = (it.target as HTMLInputElement).value
|
||||
try {
|
||||
if(value.isEmpty()){
|
||||
props.root.remove(props.name)
|
||||
}
|
||||
props.root.setValue(props.name, value.asValue())
|
||||
} catch (ex: Exception) {
|
||||
console.error("Can't set config property ${props.name} to $value")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun RBuilder.render() {
|
||||
val item = props.root[props.name]
|
||||
val descriptorItem = props.descriptor?.get(props.name)
|
||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||
val defaultItem = props.default?.get(props.name)
|
||||
val actualItem = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||
val token = props.name.last()
|
||||
val token = props.name.last()?.toString() ?: "Properties"
|
||||
|
||||
div("d-inline-block text-truncate") {
|
||||
when (actualItem) {
|
||||
null -> {
|
||||
}
|
||||
is MetaItem.ValueItem -> {
|
||||
i("tree-caret") { }
|
||||
}
|
||||
is MetaItem.NodeItem -> {
|
||||
i("tree-caret fa fa-caret-right") {
|
||||
when (actualItem) {
|
||||
is MetaItem.NodeItem -> {
|
||||
div("d-inline-block text-truncate") {
|
||||
span("tree-caret") {
|
||||
attrs {
|
||||
if (state.expanded) {
|
||||
classes += "rotate"
|
||||
classes += "tree-caret-down"
|
||||
}
|
||||
onClickFunction = onClick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
label("tree-label") {
|
||||
+token.toString()
|
||||
attrs {
|
||||
if (item == null) {
|
||||
classes += "tree-label-inactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actualItem is MetaItem.NodeItem && state.expanded) {
|
||||
ul("tree") {
|
||||
val keys = buildList<NameToken> {
|
||||
item?.node?.items?.keys?.let { addAll(it) }
|
||||
defaultItem?.node?.items?.keys?.let { addAll(it) }
|
||||
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
|
||||
add(NameToken(it))
|
||||
}
|
||||
}
|
||||
keys.forEach { token ->
|
||||
li("tree-item") {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = props.root
|
||||
name = props.name + token
|
||||
this.default = props.default
|
||||
this.descriptor = props.descriptor
|
||||
listen = false
|
||||
}
|
||||
span("tree-label") {
|
||||
+token
|
||||
attrs {
|
||||
if (item == null) {
|
||||
classes += "tree-label-inactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (actualItem is MetaItem.ValueItem) {
|
||||
div("row") {
|
||||
div("col") {
|
||||
label("tree-label") {
|
||||
+token.toString()
|
||||
if (state.expanded) {
|
||||
ul("tree") {
|
||||
val keys = buildSet<NameToken> {
|
||||
item?.node?.items?.keys?.let { addAll(it) }
|
||||
defaultItem?.node?.items?.keys?.let { addAll(it) }
|
||||
(descriptorItem as? NodeDescriptor)?.items?.keys?.forEach {
|
||||
add(NameToken(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
div("col") {
|
||||
input(type = InputType.text) {
|
||||
attrs {
|
||||
value = actualItem.value.toString()
|
||||
onChangeFunction = {
|
||||
try {
|
||||
props.root.setValue(props.name, value.asValue())
|
||||
} catch (ex: Exception) {
|
||||
console.error("Can't set config property $name to $value")
|
||||
|
||||
keys.forEach { token ->
|
||||
li("tree-item") {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = props.root
|
||||
name = props.name + token
|
||||
this.default = props.default
|
||||
this.descriptor = props.descriptor
|
||||
listen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+actualItem.value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
is MetaItem.ValueItem -> {
|
||||
div("d-inline-block text-truncate") {
|
||||
div("row") {
|
||||
div("col") {
|
||||
p("tree-label") {
|
||||
+token
|
||||
attrs {
|
||||
if (item == null) {
|
||||
classes += "tree-label-inactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
div("col") {
|
||||
div("float-right") {
|
||||
input(type = InputType.text) {
|
||||
attrs {
|
||||
defaultValue = actualItem.value.string
|
||||
onChangeFunction = onValueChange
|
||||
}
|
||||
}
|
||||
//+actualItem.value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,3 +195,15 @@ fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, de
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun RBuilder.configEditor(obj: Configurable, descriptor: NodeDescriptor? = obj.descriptor, default: Meta? = null) {
|
||||
child(ConfigEditorComponent::class) {
|
||||
attrs {
|
||||
root = obj.config
|
||||
name = Name.EMPTY
|
||||
this.descriptor = descriptor
|
||||
this.default = default
|
||||
listen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,15 +41,15 @@ class ObjectTreeComponent : RComponent<ObjectTreeProps, TreeState>() {
|
||||
//display as node if any child is visible
|
||||
if (obj is VisualGroup && obj.children.keys.any { !it.body.startsWith("@") }) {
|
||||
div("d-inline-block text-truncate") {
|
||||
span("objTree-caret") {
|
||||
span("tree-caret") {
|
||||
attrs {
|
||||
if (state.expanded) {
|
||||
classes += "objTree-caret-down"
|
||||
classes += "tree-caret-down"
|
||||
}
|
||||
onClickFunction = onClick
|
||||
}
|
||||
}
|
||||
label("objTree-label") {
|
||||
a("#",classes = "tree-label") {
|
||||
+token
|
||||
attrs {
|
||||
onClickFunction = { props.clickCallback(props.name) }
|
||||
@ -57,12 +57,12 @@ class ObjectTreeComponent : RComponent<ObjectTreeProps, TreeState>() {
|
||||
}
|
||||
}
|
||||
if (state.expanded) {
|
||||
ul("objTree-subtree") {
|
||||
ul("tree") {
|
||||
obj.children.entries
|
||||
.filter { !it.key.toString().startsWith("@") } // ignore statics and other hidden children
|
||||
.sortedBy { (it.value as? VisualGroup)?.isEmpty ?: true }
|
||||
.forEach { (childToken, child) ->
|
||||
li {
|
||||
li("tree-item") {
|
||||
child(ObjectTreeComponent::class) {
|
||||
attrs {
|
||||
name = props.name + childToken
|
||||
@ -76,8 +76,8 @@ class ObjectTreeComponent : RComponent<ObjectTreeProps, TreeState>() {
|
||||
}
|
||||
} else {
|
||||
div("d-inline-block text-truncate") {
|
||||
span("objTree-leaf") {}
|
||||
label("objTree-label") {
|
||||
span("tree-leaf") {}
|
||||
a("#",classes = "tree-label") {
|
||||
+token
|
||||
attrs {
|
||||
onClickFunction = { props.clickCallback(props.name) }
|
||||
|
@ -93,7 +93,7 @@ fun RBuilder.visualPropertyEditor(
|
||||
}
|
||||
}
|
||||
}
|
||||
configEditor(item.config, descriptor, Meta(default))
|
||||
configEditor(item, descriptor, Meta(default))
|
||||
}
|
||||
|
||||
fun Element.visualPropertyEditor(
|
||||
|
@ -1,40 +1,42 @@
|
||||
/* Remove default bullets */
|
||||
ul, .objTree-subtree {
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.objTree-caret {
|
||||
.tree-caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.objTree-caret::before {
|
||||
.tree-caret::before {
|
||||
content: "\25B6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.objTree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
.tree-leaf{
|
||||
user-select: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
i, .tree-caret{
|
||||
.tree-leaf::before {
|
||||
content: "\25C6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.rotate {
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.tree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tree-label-inactive {
|
||||
background-color: lightgrey;
|
||||
color: gray;
|
||||
}
|
||||
display: inline-block;
|
||||
}
|
@ -26,4 +26,14 @@ class TestConvertor {
|
||||
val visual = xml.toVisual()
|
||||
// println(visual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimple() {
|
||||
val stream = javaClass.getResourceAsStream("/gdml/simple1.gdml")
|
||||
|
||||
val xmlReader = StAXReader(stream, "UTF-8")
|
||||
val xml = GDML.format.parse(GDML.serializer(), xmlReader)
|
||||
val visual = xml.toVisual()
|
||||
println(visual.stringify())
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<gdml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd">
|
||||
<define/>
|
||||
<materials>
|
||||
<material name="Vacuum" Z="1">
|
||||
<D unit="g/cm3" value="0"/>
|
||||
<atom unit="g/mole" value="0"/>
|
||||
</material>
|
||||
<material name="Al" Z="13">
|
||||
<D unit="g/cm3" value="2.7000000000000002"/>
|
||||
<atom unit="g/mole" value="26.98"/>
|
||||
</material>
|
||||
</materials>
|
||||
<solids>
|
||||
<box name="R" x="50" y="50" z="10" lunit="cm"/>
|
||||
</solids>
|
||||
<structure>
|
||||
<volume name="R">
|
||||
<materialref ref="Vacuum"/>
|
||||
<solidref ref="R"/>
|
||||
</volume>
|
||||
</structure>
|
||||
<setup name="default" version="1.0">
|
||||
<world ref="R"/>
|
||||
</setup>
|
||||
</gdml>
|
@ -47,23 +47,17 @@ class Material3D : Scheme() {
|
||||
val descriptor by lazy {
|
||||
//must be lazy to avoid initialization bug
|
||||
NodeDescriptor {
|
||||
defineValue(VisualObject3D.VISIBLE_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(true)
|
||||
defineValue(COLOR_KEY) {
|
||||
type(ValueType.STRING, ValueType.NUMBER)
|
||||
default("#ffffff")
|
||||
}
|
||||
defineNode(MATERIAL_KEY) {
|
||||
defineValue(COLOR_KEY) {
|
||||
type(ValueType.STRING, ValueType.NUMBER)
|
||||
default("#ffffff")
|
||||
}
|
||||
defineValue(OPACITY_KEY) {
|
||||
type(ValueType.NUMBER)
|
||||
default(1.0)
|
||||
}
|
||||
defineValue(WIREFRAME_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(false)
|
||||
}
|
||||
defineValue(OPACITY_KEY) {
|
||||
type(ValueType.NUMBER)
|
||||
default(1.0)
|
||||
}
|
||||
defineValue(WIREFRAME_KEY) {
|
||||
type(ValueType.BOOLEAN)
|
||||
default(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import hep.dataforge.names.toName
|
||||
import hep.dataforge.vis.SimpleVisualGroup
|
||||
import hep.dataforge.vis.Visual
|
||||
import hep.dataforge.vis.VisualObject
|
||||
import kotlinx.serialization.UnstableDefault
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
@ -53,6 +54,7 @@ class Visual3D(meta: Meta) : AbstractPlugin(meta) {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableDefault::class)
|
||||
internal val json = Json(
|
||||
JsonConfiguration(
|
||||
prettyPrint = true,
|
||||
|
@ -17,12 +17,12 @@
|
||||
}
|
||||
|
||||
/* Remove default bullets */
|
||||
ul, .objTree-subtree {
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.objTree-caret {
|
||||
.tree-caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
@ -32,7 +32,7 @@ ul, .objTree-subtree {
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.objTree-caret::before {
|
||||
.tree-caret::before {
|
||||
content: "\25B6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
@ -47,6 +47,6 @@ ul, .objTree-subtree {
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.objTree-caret-down::before {
|
||||
.tree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
@ -13,9 +13,19 @@ kotlin {
|
||||
|
||||
val installJS = tasks.getByName("jsBrowserDistribution")
|
||||
|
||||
js{
|
||||
browser {
|
||||
dceTask {
|
||||
dceOptions {
|
||||
keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvm {
|
||||
withJava()
|
||||
compilations.findByName("jvmMain").apply {
|
||||
compilations.findByName("main").apply {
|
||||
tasks.getByName<ProcessResources>("jvmProcessResources") {
|
||||
dependsOn(installJS)
|
||||
afterEvaluate {
|
||||
|
@ -10,12 +10,8 @@ import hep.dataforge.vis.VisualObject
|
||||
import hep.dataforge.vis.editor.card
|
||||
import hep.dataforge.vis.editor.objectTree
|
||||
import hep.dataforge.vis.editor.visualPropertyEditor
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_COLOR_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_OPACITY_KEY
|
||||
import hep.dataforge.vis.spatial.Material3D.Companion.MATERIAL_WIREFRAME_KEY
|
||||
import hep.dataforge.vis.spatial.Visual3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D
|
||||
import hep.dataforge.vis.spatial.VisualObject3D.Companion.VISIBLE_KEY
|
||||
import hep.dataforge.vis.spatial.three.ThreePlugin
|
||||
import hep.dataforge.vis.spatial.three.displayCanvasControls
|
||||
import hep.dataforge.vis.spatial.three.output
|
||||
@ -92,28 +88,8 @@ private class MMDemoApp : Application {
|
||||
visual is VisualGroup -> visual[name] ?: return
|
||||
else -> return
|
||||
}
|
||||
editorElement.visualPropertyEditor(name, child) {
|
||||
VISIBLE_KEY put true
|
||||
if (child is VisualObject3D) {
|
||||
MATERIAL_COLOR_KEY put "#ffffff"
|
||||
MATERIAL_OPACITY_KEY put 1.0
|
||||
MATERIAL_WIREFRAME_KEY put false
|
||||
}
|
||||
}
|
||||
// editorElement.displayPropertyEditor(name, child) { item ->
|
||||
// //val descriptorMeta = Material3D.descriptor
|
||||
//
|
||||
// val properties = item.allProperties()
|
||||
// val bottom = Meta {
|
||||
// VISIBLE_KEY put (item.visible ?: true)
|
||||
// if (item is VisualObject3D) {
|
||||
// MATERIAL_COLOR_KEY put "#ffffff"
|
||||
// MATERIAL_OPACITY_KEY put 1.0
|
||||
// MATERIAL_WIREFRAME_KEY put false
|
||||
// }
|
||||
// }
|
||||
// properties.withBottom(bottom)
|
||||
// }
|
||||
editorElement.visualPropertyEditor(name, child, descriptor = VisualObject3D.descriptor)
|
||||
|
||||
}
|
||||
|
||||
// canvas.clickListener = ::selectElement
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,48 +1,40 @@
|
||||
.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, .objTree-subtree {
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.objTree-caret {
|
||||
.tree-caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
|
||||
.objTree-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.objTree-caret::before {
|
||||
.tree-caret::before {
|
||||
content: "\25B6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.objTree-leaf::before {
|
||||
.tree-leaf{
|
||||
user-select: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tree-leaf::before {
|
||||
content: "\25C6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.objTree-caret-down::before {
|
||||
.tree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.tree-label-inactive {
|
||||
color: gray;
|
||||
}
|
@ -4,10 +4,8 @@
|
||||
<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>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<link rel="stylesheet" href="css/jsoneditor.min.css">
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
<body class="testApp">
|
||||
|
@ -17,6 +17,7 @@ import io.ktor.routing.get
|
||||
import io.ktor.serialization.json
|
||||
import io.ktor.server.cio.CIO
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.apache.commons.math3.random.JDKRandomGenerator
|
||||
import ru.mipt.npm.muon.monitor.Model
|
||||
import ru.mipt.npm.muon.monitor.sim.Cos2TrackGenerator
|
||||
@ -56,6 +57,7 @@ fun Application.module() {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtorExperimentalAPI::class)
|
||||
fun main() {
|
||||
embeddedServer(CIO, 8080, host = "localhost", module = Application::module).start(wait = true)
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
/* Remove default bullets */
|
||||
ul, .objTree-subtree {
|
||||
ul, .tree {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* Style the caret/arrow */
|
||||
.objTree-caret {
|
||||
.tree-caret {
|
||||
cursor: pointer;
|
||||
user-select: none; /* Prevent text selection */
|
||||
}
|
||||
|
||||
/* Create the caret/arrow with a unicode, and style it */
|
||||
.objTree-caret::before {
|
||||
.tree-caret::before {
|
||||
content: "\25B6";
|
||||
color: black;
|
||||
display: inline-block;
|
||||
@ -18,7 +18,7 @@ ul, .objTree-subtree {
|
||||
}
|
||||
|
||||
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
||||
.objTree-caret-down::before {
|
||||
.tree-caret-down::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user