Type-safe react hooks
This commit is contained in:
parent
63f2713875
commit
2d79e8e422
@ -6,6 +6,10 @@ val dataforgeVersion: String by rootProject.extra
|
||||
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
useCommonJs()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
|
@ -1,9 +1,11 @@
|
||||
package hep.dataforge.vis
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.asName
|
||||
import hep.dataforge.values.Value
|
||||
import hep.dataforge.values.ValueType
|
||||
import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@ -19,15 +21,15 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
|
||||
protected abstract var properties: Config?
|
||||
|
||||
override var styles: List<String>
|
||||
final override var styles: List<String>
|
||||
get() = properties?.get(STYLE_KEY).stringList
|
||||
set(value) {
|
||||
//val allStyles = (field + value).distinct()
|
||||
setProperty(STYLE_KEY, Value.of(value))
|
||||
updateStyles(value)
|
||||
}
|
||||
|
||||
protected fun updateStyles(names: List<String>) {
|
||||
styleCache = null
|
||||
names.mapNotNull { findStyle(it) }.asSequence()
|
||||
.flatMap { it.items.asSequence() }
|
||||
.distinctBy { it.key }
|
||||
@ -77,7 +79,7 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
/**
|
||||
* All available properties in a layered form
|
||||
*/
|
||||
override fun allProperties(): Laminate = Laminate(properties, mergedStyles)
|
||||
override fun allProperties(): Laminate = Laminate(properties, mergedStyles, parent?.allProperties())
|
||||
|
||||
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||
return if (inherit) {
|
||||
@ -86,6 +88,16 @@ abstract class AbstractVisualObject : VisualObject {
|
||||
properties?.get(name) ?: mergedStyles[name]
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val descriptor = NodeDescriptor {
|
||||
defineValue(STYLE_KEY){
|
||||
type(ValueType.STRING)
|
||||
multiple = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||
|
@ -1,11 +1,28 @@
|
||||
package hep.dataforge.js
|
||||
|
||||
import react.RBuilder
|
||||
import react.*
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class RFBuilder : RBuilder()
|
||||
|
||||
fun <T> RBuilder.initState(init: () -> T): ReadWriteProperty<Any?, T> =
|
||||
/**
|
||||
* Get functional component from [func]
|
||||
*/
|
||||
fun <P : RProps> component(
|
||||
func: RFBuilder.(props: P) -> Unit
|
||||
): FunctionalComponent<P> {
|
||||
return { props: P ->
|
||||
val nodes = RFBuilder().apply { func(props) }.childList
|
||||
when (nodes.size) {
|
||||
0 -> null
|
||||
1 -> nodes.first()
|
||||
else -> createElement(Fragment, kotlinext.js.js {}, *nodes.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> RFBuilder.initState(init: () -> T): ReadWriteProperty<Any?, T> =
|
||||
object : ReadWriteProperty<Any?, T> {
|
||||
val pair = react.useState(init)
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
@ -17,3 +34,5 @@ fun <T> RBuilder.initState(init: () -> T): ReadWriteProperty<Any?, T> =
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> RFBuilder.memoize(vararg deps: dynamic, builder: () -> T): T = useMemo(builder, deps)
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
package hep.dataforge.vis.editor
|
||||
|
||||
import hep.dataforge.js.RFBuilder
|
||||
import hep.dataforge.js.component
|
||||
import hep.dataforge.js.initState
|
||||
import hep.dataforge.js.memoize
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.descriptors.*
|
||||
import hep.dataforge.names.Name
|
||||
@ -37,11 +40,12 @@ interface ConfigEditorProps : RProps {
|
||||
var descriptor: NodeDescriptor?
|
||||
}
|
||||
|
||||
private fun RBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
var expanded: Boolean by initState { true }
|
||||
val item = props.root[props.name]
|
||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
||||
val defaultItem = props.default?.get(props.name)
|
||||
val item = memoize(props.root, props.name) { props.root[props.name] }
|
||||
val descriptorItem: ItemDescriptor? = memoize(props.descriptor, props.name) { props.descriptor?.get(props.name) }
|
||||
val defaultItem = memoize(props.default, props.name) { props.default?.get(props.name) }
|
||||
val actualItem: MetaItem<Meta>? = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||
|
||||
val token = props.name.last()?.toString() ?: "Properties"
|
||||
|
||||
@ -60,8 +64,6 @@ private fun RBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
return@useEffectWithCleanup { props.root.removeListener(this) }
|
||||
}
|
||||
|
||||
val actualItem: MetaItem<Meta>? = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
||||
|
||||
val expanderClick: (Event) -> Unit = {
|
||||
expanded = !expanded
|
||||
}
|
||||
@ -162,7 +164,7 @@ private fun RBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||
}
|
||||
}
|
||||
|
||||
val ConfigEditor: FunctionalComponent<ConfigEditorProps> = functionalComponent { configEditorItem(it) }
|
||||
val ConfigEditor: FunctionalComponent<ConfigEditorProps> = component { configEditorItem(it) }
|
||||
|
||||
fun RBuilder.configEditor(
|
||||
config: Config,
|
||||
|
@ -1,6 +1,8 @@
|
||||
package hep.dataforge.vis.editor
|
||||
|
||||
import hep.dataforge.js.RFBuilder
|
||||
import hep.dataforge.js.card
|
||||
import hep.dataforge.js.component
|
||||
import hep.dataforge.js.initState
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.plus
|
||||
@ -26,7 +28,7 @@ interface TreeState : RState {
|
||||
var expanded: Boolean
|
||||
}
|
||||
|
||||
private fun RBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
var expanded: Boolean by initState{ props.selected?.startsWith(props.name) ?: false }
|
||||
|
||||
val onClick: (Event) -> Unit = {
|
||||
@ -90,7 +92,7 @@ private fun RBuilder.objectTree(props: ObjectTreeProps): Unit {
|
||||
}
|
||||
}
|
||||
|
||||
val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent { props ->
|
||||
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props ->
|
||||
objectTree(props)
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package hep.dataforge.vis.editor
|
||||
|
||||
import hep.dataforge.js.component
|
||||
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
@ -11,11 +12,11 @@ import org.w3c.dom.HTMLInputElement
|
||||
import org.w3c.dom.HTMLSelectElement
|
||||
import org.w3c.dom.events.Event
|
||||
import react.RProps
|
||||
import react.RState
|
||||
import react.dom.div
|
||||
import react.dom.input
|
||||
import react.dom.option
|
||||
import react.dom.select
|
||||
import react.functionalComponent
|
||||
|
||||
interface ValueChooserProps : RProps {
|
||||
var value: Value
|
||||
@ -23,7 +24,35 @@ interface ValueChooserProps : RProps {
|
||||
var valueChanged: (Value?) -> Unit
|
||||
}
|
||||
|
||||
val ValueChooser = functionalComponent<ValueChooserProps> { props ->
|
||||
interface ValueChooserState : RState {
|
||||
var value: Value
|
||||
}
|
||||
|
||||
//class TextValueChooser(props: ValueChooserProps) : RComponent<ValueChooserProps, ValueChooserState>(props) {
|
||||
//
|
||||
// override fun ValueChooserState.init(props: ValueChooserProps) {
|
||||
// this.value = props.value
|
||||
// }
|
||||
//
|
||||
// val valueChanged: (Event) -> Unit = {
|
||||
// val res = (it.target as HTMLInputElement).value.asValue()
|
||||
// setState {
|
||||
// this.value = res
|
||||
// }
|
||||
// props.valueChanged(res)
|
||||
// }
|
||||
//
|
||||
// override fun RBuilder.render() {
|
||||
// input(type = InputType.text, classes = "float-right") {
|
||||
// attrs {
|
||||
// this.value = state.value.string
|
||||
// onChangeFunction = valueChanged
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
val ValueChooser = component<ValueChooserProps> { props ->
|
||||
// var state by initState {props.value }
|
||||
val descriptor = props.descriptor
|
||||
|
||||
|
@ -79,7 +79,8 @@ class Proxy private constructor(
|
||||
?: error("Prototype with name $name not found in $this")
|
||||
}
|
||||
|
||||
override fun allProperties(): Laminate = Laminate(properties, mergedStyles, prototype.allProperties())
|
||||
override fun allProperties(): Laminate =
|
||||
Laminate(properties, mergedStyles, prototype.allProperties(), parent?.allProperties())
|
||||
|
||||
override fun attachChildren() {
|
||||
//do nothing
|
||||
@ -135,7 +136,8 @@ class Proxy private constructor(
|
||||
//do nothing
|
||||
}
|
||||
|
||||
override fun allProperties(): Laminate = Laminate(properties, mergedStyles, prototype.allProperties())
|
||||
override fun allProperties(): Laminate =
|
||||
Laminate(properties, mergedStyles, prototype.allProperties(), parent?.allProperties())
|
||||
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,12 @@ interface VisualObject3D : VisualObject {
|
||||
|
||||
defineItem(Material3D.MATERIAL_KEY.toString(), Material3D.descriptor)
|
||||
|
||||
//TODO replace by descriptor merge
|
||||
defineValue(VisualObject.STYLE_KEY){
|
||||
type(ValueType.STRING)
|
||||
multiple = true
|
||||
}
|
||||
|
||||
// Material3D.MATERIAL_COLOR_KEY put "#ffffff"
|
||||
// Material3D.MATERIAL_OPACITY_KEY put 1.0
|
||||
// Material3D.MATERIAL_WIREFRAME_KEY put false
|
||||
|
@ -2,6 +2,7 @@ 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.initState
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
@ -21,7 +22,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.html.js.onClickFunction
|
||||
import react.RProps
|
||||
import react.dom.*
|
||||
import react.functionalComponent
|
||||
import kotlin.math.PI
|
||||
|
||||
interface MMAppProps : RProps {
|
||||
@ -39,7 +39,7 @@ private val canvasConfig = Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
val MMApp = functionalComponent<MMAppProps> { props ->
|
||||
val MMApp = component<MMAppProps> { props ->
|
||||
var selected by initState { props.selected }
|
||||
var canvas: ThreeCanvas? by initState { null }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user