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")
|
//val kvisionVersion: String by rootProject.extra("2.0.0-M1")
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
js {
|
||||||
|
useCommonJs()
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package hep.dataforge.vis
|
package hep.dataforge.vis
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.meta.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
import hep.dataforge.values.Value
|
import hep.dataforge.values.Value
|
||||||
|
import hep.dataforge.values.ValueType
|
||||||
import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY
|
import hep.dataforge.vis.VisualObject.Companion.STYLE_KEY
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
|
|
||||||
@ -19,15 +21,15 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
|
|
||||||
protected abstract var properties: Config?
|
protected abstract var properties: Config?
|
||||||
|
|
||||||
override var styles: List<String>
|
final override var styles: List<String>
|
||||||
get() = properties?.get(STYLE_KEY).stringList
|
get() = properties?.get(STYLE_KEY).stringList
|
||||||
set(value) {
|
set(value) {
|
||||||
//val allStyles = (field + value).distinct()
|
|
||||||
setProperty(STYLE_KEY, Value.of(value))
|
setProperty(STYLE_KEY, Value.of(value))
|
||||||
updateStyles(value)
|
updateStyles(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun updateStyles(names: List<String>) {
|
protected fun updateStyles(names: List<String>) {
|
||||||
|
styleCache = null
|
||||||
names.mapNotNull { findStyle(it) }.asSequence()
|
names.mapNotNull { findStyle(it) }.asSequence()
|
||||||
.flatMap { it.items.asSequence() }
|
.flatMap { it.items.asSequence() }
|
||||||
.distinctBy { it.key }
|
.distinctBy { it.key }
|
||||||
@ -77,7 +79,7 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
/**
|
/**
|
||||||
* All available properties in a layered form
|
* 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<*>? {
|
override fun getProperty(name: Name, inherit: Boolean): MetaItem<*>? {
|
||||||
return if (inherit) {
|
return if (inherit) {
|
||||||
@ -86,6 +88,16 @@ abstract class AbstractVisualObject : VisualObject {
|
|||||||
properties?.get(name) ?: mergedStyles[name]
|
properties?.get(name) ?: mergedStyles[name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val descriptor = NodeDescriptor {
|
||||||
|
defineValue(STYLE_KEY){
|
||||||
|
type(ValueType.STRING)
|
||||||
|
multiple = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
//fun VisualObject.findStyle(styleName: Name): Meta? {
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
package hep.dataforge.js
|
package hep.dataforge.js
|
||||||
|
|
||||||
import react.RBuilder
|
import react.*
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
import kotlin.reflect.KProperty
|
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> {
|
object : ReadWriteProperty<Any?, T> {
|
||||||
val pair = react.useState(init)
|
val pair = react.useState(init)
|
||||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
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
|
package hep.dataforge.vis.editor
|
||||||
|
|
||||||
|
import hep.dataforge.js.RFBuilder
|
||||||
|
import hep.dataforge.js.component
|
||||||
import hep.dataforge.js.initState
|
import hep.dataforge.js.initState
|
||||||
|
import hep.dataforge.js.memoize
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.meta.descriptors.*
|
import hep.dataforge.meta.descriptors.*
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
@ -37,11 +40,12 @@ interface ConfigEditorProps : RProps {
|
|||||||
var descriptor: NodeDescriptor?
|
var descriptor: NodeDescriptor?
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RBuilder.configEditorItem(props: ConfigEditorProps) {
|
private fun RFBuilder.configEditorItem(props: ConfigEditorProps) {
|
||||||
var expanded: Boolean by initState { true }
|
var expanded: Boolean by initState { true }
|
||||||
val item = props.root[props.name]
|
val item = memoize(props.root, props.name) { props.root[props.name] }
|
||||||
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
|
val descriptorItem: ItemDescriptor? = memoize(props.descriptor, props.name) { props.descriptor?.get(props.name) }
|
||||||
val defaultItem = props.default?.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"
|
val token = props.name.last()?.toString() ?: "Properties"
|
||||||
|
|
||||||
@ -60,8 +64,6 @@ private fun RBuilder.configEditorItem(props: ConfigEditorProps) {
|
|||||||
return@useEffectWithCleanup { props.root.removeListener(this) }
|
return@useEffectWithCleanup { props.root.removeListener(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val actualItem: MetaItem<Meta>? = item ?: defaultItem ?: descriptorItem?.defaultItem()
|
|
||||||
|
|
||||||
val expanderClick: (Event) -> Unit = {
|
val expanderClick: (Event) -> Unit = {
|
||||||
expanded = !expanded
|
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(
|
fun RBuilder.configEditor(
|
||||||
config: Config,
|
config: Config,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package hep.dataforge.vis.editor
|
package hep.dataforge.vis.editor
|
||||||
|
|
||||||
|
import hep.dataforge.js.RFBuilder
|
||||||
import hep.dataforge.js.card
|
import hep.dataforge.js.card
|
||||||
|
import hep.dataforge.js.component
|
||||||
import hep.dataforge.js.initState
|
import hep.dataforge.js.initState
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.plus
|
import hep.dataforge.names.plus
|
||||||
@ -26,7 +28,7 @@ interface TreeState : RState {
|
|||||||
var expanded: Boolean
|
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 }
|
var expanded: Boolean by initState{ props.selected?.startsWith(props.name) ?: false }
|
||||||
|
|
||||||
val onClick: (Event) -> Unit = {
|
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)
|
objectTree(props)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package hep.dataforge.vis.editor
|
package hep.dataforge.vis.editor
|
||||||
|
|
||||||
|
import hep.dataforge.js.component
|
||||||
import hep.dataforge.meta.descriptors.ValueDescriptor
|
import hep.dataforge.meta.descriptors.ValueDescriptor
|
||||||
import hep.dataforge.meta.get
|
import hep.dataforge.meta.get
|
||||||
import hep.dataforge.meta.string
|
import hep.dataforge.meta.string
|
||||||
@ -11,11 +12,11 @@ import org.w3c.dom.HTMLInputElement
|
|||||||
import org.w3c.dom.HTMLSelectElement
|
import org.w3c.dom.HTMLSelectElement
|
||||||
import org.w3c.dom.events.Event
|
import org.w3c.dom.events.Event
|
||||||
import react.RProps
|
import react.RProps
|
||||||
|
import react.RState
|
||||||
import react.dom.div
|
import react.dom.div
|
||||||
import react.dom.input
|
import react.dom.input
|
||||||
import react.dom.option
|
import react.dom.option
|
||||||
import react.dom.select
|
import react.dom.select
|
||||||
import react.functionalComponent
|
|
||||||
|
|
||||||
interface ValueChooserProps : RProps {
|
interface ValueChooserProps : RProps {
|
||||||
var value: Value
|
var value: Value
|
||||||
@ -23,7 +24,35 @@ interface ValueChooserProps : RProps {
|
|||||||
var valueChanged: (Value?) -> Unit
|
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 }
|
// var state by initState {props.value }
|
||||||
val descriptor = props.descriptor
|
val descriptor = props.descriptor
|
||||||
|
|
||||||
|
@ -79,7 +79,8 @@ class Proxy private constructor(
|
|||||||
?: error("Prototype with name $name not found in $this")
|
?: 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() {
|
override fun attachChildren() {
|
||||||
//do nothing
|
//do nothing
|
||||||
@ -135,7 +136,8 @@ class Proxy private constructor(
|
|||||||
//do nothing
|
//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)
|
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_COLOR_KEY put "#ffffff"
|
||||||
// Material3D.MATERIAL_OPACITY_KEY put 1.0
|
// Material3D.MATERIAL_OPACITY_KEY put 1.0
|
||||||
// Material3D.MATERIAL_WIREFRAME_KEY put false
|
// Material3D.MATERIAL_WIREFRAME_KEY put false
|
||||||
|
@ -2,6 +2,7 @@ package ru.mipt.npm.muon.monitor
|
|||||||
|
|
||||||
import hep.dataforge.context.Context
|
import hep.dataforge.context.Context
|
||||||
import hep.dataforge.js.card
|
import hep.dataforge.js.card
|
||||||
|
import hep.dataforge.js.component
|
||||||
import hep.dataforge.js.initState
|
import hep.dataforge.js.initState
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.NameToken
|
import hep.dataforge.names.NameToken
|
||||||
@ -21,7 +22,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.html.js.onClickFunction
|
import kotlinx.html.js.onClickFunction
|
||||||
import react.RProps
|
import react.RProps
|
||||||
import react.dom.*
|
import react.dom.*
|
||||||
import react.functionalComponent
|
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
|
|
||||||
interface MMAppProps : RProps {
|
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 selected by initState { props.selected }
|
||||||
var canvas: ThreeCanvas? by initState { null }
|
var canvas: ThreeCanvas? by initState { null }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user