Refactor react ThreeCanvas to functional component

This commit is contained in:
Alexander Nozik 2020-10-30 10:02:49 +03:00
parent 3edb00b6bf
commit aaad836567
14 changed files with 162 additions and 149 deletions

View File

@ -7,7 +7,6 @@ import hep.dataforge.vision.Vision
import hep.dataforge.vision.VisionGroup import hep.dataforge.vision.VisionGroup
import hep.dataforge.vision.bootstrap.* import hep.dataforge.vision.bootstrap.*
import hep.dataforge.vision.gdml.toVision import hep.dataforge.vision.gdml.toVision
import hep.dataforge.vision.react.component
import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.react.objectTree
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.SolidGroup
@ -22,11 +21,8 @@ import kscience.gdml.GDML
import kscience.gdml.decodeFromString import kscience.gdml.decodeFromString
import org.w3c.files.FileReader import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
import react.RProps import react.*
import react.dom.h1 import react.dom.h1
import react.getValue
import react.setValue
import react.useState
import styled.css import styled.css
external interface GDMLAppProps : RProps { external interface GDMLAppProps : RProps {
@ -44,7 +40,7 @@ external interface GDMLAppProps : RProps {
//} //}
@JsExport @JsExport
val GDMLApp = component<GDMLAppProps> { props -> val GDMLApp = functionalComponent<GDMLAppProps>("GDMLApp") { props ->
var selected by useState { props.selected } var selected by useState { props.selected }
var canvas: ThreeCanvas? by useState { null } var canvas: ThreeCanvas? by useState { null }
var vision: Vision? by useState { props.rootObject } var vision: Vision? by useState { props.rootObject }
@ -113,16 +109,14 @@ val GDMLApp = component<GDMLAppProps> { props ->
+"order-xl-2" +"order-xl-2"
} }
//canvas //canvas
(vision as? Solid)?.let { visual3D -> child(ThreeCanvasComponent) {
child(ThreeCanvasComponent::class) { attrs {
attrs { this.context = props.context
this.context = props.context this.obj = vision as? Solid
this.obj = visual3D this.selected = selected
this.selected = selected this.clickCallback = select
this.clickCallback = select this.canvasCallback = {
this.canvasCallback = { canvas = it
canvas = it
}
} }
} }
} }

View File

@ -8,7 +8,6 @@ import hep.dataforge.names.length
import hep.dataforge.vision.Vision import hep.dataforge.vision.Vision
import hep.dataforge.vision.bootstrap.canvasControls import hep.dataforge.vision.bootstrap.canvasControls
import hep.dataforge.vision.bootstrap.card import hep.dataforge.vision.bootstrap.card
import hep.dataforge.vision.react.component
import hep.dataforge.vision.react.configEditor import hep.dataforge.vision.react.configEditor
import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.react.objectTree
import hep.dataforge.vision.solid.specifications.Camera import hep.dataforge.vision.solid.specifications.Camera
@ -20,11 +19,10 @@ import io.ktor.client.request.get
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.html.js.onClickFunction import kotlinx.html.js.onClickFunction
import react.RProps import react.*
import react.dom.* import react.dom.*
import react.getValue import styled.css
import react.setValue import styled.styledDiv
import react.useState
import kotlin.math.PI import kotlin.math.PI
external interface MMAppProps : RProps { external interface MMAppProps : RProps {
@ -43,8 +41,8 @@ private val canvasConfig = Canvas3DOptions {
} }
@JsExport @JsExport
val MMApp = component<MMAppProps> { props -> val MMApp = functionalComponent<MMAppProps>("Muon monitor") { props ->
var selected by useState { props.selected } var selected by useState { props.selected }
var canvas: ThreeCanvas? by useState { null } var canvas: ThreeCanvas? by useState { null }
val select: (Name?) -> Unit = { val select: (Name?) -> Unit = {
@ -59,7 +57,12 @@ val MMApp = component<MMAppProps> { props ->
} }
} }
div("row") { div("row") {
div("col-lg-3 px-0 overflow-auto") { styledDiv {
css {
+"col-lg-3"
+"px-0"
+"overflow-auto"
}
//tree //tree
card("Object tree") { card("Object tree") {
objectTree(root, selected, select) objectTree(root, selected, select)
@ -67,7 +70,7 @@ val MMApp = component<MMAppProps> { props ->
} }
div("col-lg-6") { div("col-lg-6") {
//canvas //canvas
child(ThreeCanvasComponent::class) { child(ThreeCanvasComponent) {
attrs { attrs {
this.context = props.context this.context = props.context
this.obj = root this.obj = root

View File

@ -3,16 +3,25 @@ import hep.dataforge.js.Application
import hep.dataforge.js.startApplication import hep.dataforge.js.startApplication
import hep.dataforge.vision.bootstrap.visionPropertyEditor import hep.dataforge.vision.bootstrap.visionPropertyEditor
import hep.dataforge.vision.react.objectTree import hep.dataforge.vision.react.objectTree
import hep.dataforge.vision.solid.Point3D import hep.dataforge.vision.solid.*
import hep.dataforge.vision.solid.SolidGroup import hep.dataforge.vision.solid.specifications.Canvas3DOptions
import hep.dataforge.vision.solid.box import hep.dataforge.vision.solid.three.ThreeCanvasComponent
import hep.dataforge.vision.solid.group
import hep.dataforge.vision.solid.three.ThreePlugin import hep.dataforge.vision.solid.three.ThreePlugin
import hep.dataforge.vision.solid.three.threeCanvas import kotlinx.browser.document
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import react.RBuilder
import react.child
import react.dom.div import react.dom.div
import react.dom.render import react.dom.render
import kotlin.browser.document
public fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) {
child(ThreeCanvasComponent) {
attrs {
this.obj = object3D
this.options = Canvas3DOptions.invoke(options)
}
}
}
private class PlayGroundApp : Application { private class PlayGroundApp : Application {

View File

@ -19,29 +19,29 @@ public external interface ConfigEditorItemProps : RProps {
/** /**
* Root config object - always non null * Root config object - always non null
*/ */
var root: Config public var root: Config
/** /**
* Full path to the displayed node in [root]. Could be empty * Full path to the displayed node in [root]. Could be empty
*/ */
var name: Name public var name: Name
/** /**
* Root default * Root default
*/ */
var default: Meta? public var default: Meta?
/** /**
* Root descriptor * Root descriptor
*/ */
var descriptor: NodeDescriptor? public var descriptor: NodeDescriptor?
} }
private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = component { props -> private val ConfigEditorItem: FunctionalComponent<ConfigEditorItemProps> = functionalComponent("ConfigEditorItem") { props ->
configEditorItem(props) configEditorItem(props)
} }
private fun RFBuilder.configEditorItem(props: ConfigEditorItemProps) { private fun RBuilder.configEditorItem(props: ConfigEditorItemProps) {
var expanded: Boolean by useState { true } var expanded: Boolean by useState { true }
var item: MetaItem<Config>? by useState { props.root[props.name] } var item: MetaItem<Config>? by useState { props.root[props.name] }
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
@ -198,7 +198,7 @@ public external interface ConfigEditorProps : RProps {
} }
@JsExport @JsExport
public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = component { props -> public val ConfigEditor: FunctionalComponent<ConfigEditorProps> = functionalComponent("ConfigEditor") { props ->
child(ConfigEditorItem) { child(ConfigEditorItem) {
attrs { attrs {
this.key = "" this.key = ""
@ -223,7 +223,7 @@ public fun Element.configEditor(config: Config, descriptor: NodeDescriptor? = nu
} }
} }
fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) { public fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, default: Meta? = null, key: Any? = null) {
child(ConfigEditor) { child(ConfigEditor) {
attrs { attrs {
this.key = key?.toString() ?: "" this.key = key?.toString() ?: ""
@ -234,7 +234,7 @@ fun RBuilder.configEditor(config: Config, descriptor: NodeDescriptor? = null, de
} }
} }
fun RBuilder.configEditor( public fun RBuilder.configEditor(
obj: Configurable, obj: Configurable,
descriptor: NodeDescriptor? = obj.descriptor, descriptor: NodeDescriptor? = obj.descriptor,
default: Meta? = null, default: Meta? = null,

View File

@ -21,24 +21,24 @@ public external interface MetaViewerProps : RProps {
/** /**
* Root meta * Root meta
*/ */
var root: Meta public var root: Meta
/** /**
* Full path to the displayed node in [root]. Could be empty * Full path to the displayed node in [root]. Could be empty
*/ */
var name: Name public var name: Name
/** /**
* Root descriptor * Root descriptor
*/ */
var descriptor: NodeDescriptor? public var descriptor: NodeDescriptor?
} }
private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = component { props -> private val MetaViewerItem: FunctionalComponent<MetaViewerProps> = functionalComponent("MetaViewerItem") { props ->
metaViewerItem(props) metaViewerItem(props)
} }
private fun RFBuilder.metaViewerItem(props: MetaViewerProps) { private fun RBuilder.metaViewerItem(props: MetaViewerProps) {
var expanded: Boolean by useState { true } var expanded: Boolean by useState { true }
val item = props.root[props.name] val item = props.root[props.name]
val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name) val descriptorItem: ItemDescriptor? = props.descriptor?.get(props.name)
@ -137,7 +137,7 @@ private fun RFBuilder.metaViewerItem(props: MetaViewerProps) {
} }
@JsExport @JsExport
val MetaViewer = component<MetaViewerProps> { props -> public val MetaViewer:FunctionalComponent<MetaViewerProps> = functionalComponent<MetaViewerProps>("MetaViewer") { props ->
child(MetaViewerItem) { child(MetaViewerItem) {
attrs { attrs {
this.key = "" this.key = ""
@ -148,7 +148,7 @@ val MetaViewer = component<MetaViewerProps> { props ->
} }
} }
fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) { public fun RBuilder.metaViewer(meta: Meta, descriptor: NodeDescriptor? = null, key: Any? = null) {
child(MetaViewer) { child(MetaViewer) {
attrs { attrs {
this.key = key?.toString() ?: "" this.key = key?.toString() ?: ""

View File

@ -19,7 +19,7 @@ public external interface ObjectTreeProps : RProps {
public var clickCallback: (Name) -> Unit public var clickCallback: (Name) -> Unit
} }
private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit { private fun RBuilder.objectTree(props: ObjectTreeProps): Unit {
var expanded: Boolean by useState { props.selected?.startsWith(props.name) ?: false } var expanded: Boolean by useState { props.selected?.startsWith(props.name) ?: false }
val onClick: (Event) -> Unit = { val onClick: (Event) -> Unit = {
@ -103,11 +103,11 @@ private fun RFBuilder.objectTree(props: ObjectTreeProps): Unit {
} }
@JsExport @JsExport
val ObjectTree: FunctionalComponent<ObjectTreeProps> = component { props -> public val ObjectTree: FunctionalComponent<ObjectTreeProps> = functionalComponent("ObjectTree") { props ->
objectTree(props) objectTree(props)
} }
fun RBuilder.objectTree( public fun RBuilder.objectTree(
vision: Vision, vision: Vision,
selected: Name? = null, selected: Name? = null,
clickCallback: (Name) -> Unit = {} clickCallback: (Name) -> Unit = {}

View File

@ -4,11 +4,11 @@ import kotlinx.css.*
import kotlinx.css.properties.* import kotlinx.css.properties.*
import styled.StyleSheet import styled.StyleSheet
object TreeStyles : StyleSheet("treeStyles", true) { public object TreeStyles : StyleSheet("treeStyles", true) {
/** /**
* Remove default bullets * Remove default bullets
*/ */
val tree by css { public val tree by css {
paddingLeft = 8.px paddingLeft = 8.px
marginLeft = 0.px marginLeft = 0.px
listStyleType = ListStyleType.none listStyleType = ListStyleType.none
@ -17,7 +17,7 @@ object TreeStyles : StyleSheet("treeStyles", true) {
/** /**
* Style the caret/arrow * Style the caret/arrow
*/ */
val treeCaret by css { public val treeCaret by css {
cursor = Cursor.pointer cursor = Cursor.pointer
userSelect = UserSelect.none userSelect = UserSelect.none
/* Create the caret/arrow with a unicode, and style it */ /* Create the caret/arrow with a unicode, and style it */

View File

@ -6,11 +6,12 @@ import kotlinx.css.display
import kotlinx.css.flexDirection import kotlinx.css.flexDirection
import kotlinx.html.DIV import kotlinx.html.DIV
import react.RBuilder import react.RBuilder
import react.ReactElement
import styled.StyledDOMBuilder import styled.StyledDOMBuilder
import styled.css import styled.css
import styled.styledDiv import styled.styledDiv
inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit) = public inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit): ReactElement =
styledDiv { styledDiv {
css { css {
display = Display.flex display = Display.flex
@ -19,7 +20,7 @@ inline fun RBuilder.flexColumn(block: StyledDOMBuilder<DIV>.() -> Unit) =
block() block()
} }
inline fun RBuilder.flexRow(block: StyledDOMBuilder<DIV>.() -> Unit) = public inline fun RBuilder.flexRow(block: StyledDOMBuilder<DIV>.() -> Unit): ReactElement =
styledDiv { styledDiv {
css { css {
display = Display.flex display = Display.flex

View File

@ -1,24 +0,0 @@
package hep.dataforge.vision.react
import react.*
public class RFBuilder : RBuilder()
/**
* Get functional component from [func]
*/
public inline fun <P : RProps> component(
crossinline 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())
}
}
}
//
//public fun <T> RFBuilder.memoize(vararg deps: dynamic, builder: () -> T): T = useMemo(builder, deps)

View File

@ -13,9 +13,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import mu.KotlinLogging import mu.KotlinLogging
expect class Counter() { public expect class Counter() {
fun get(): Int public fun get(): Int
fun incrementAndGet(): Int public fun incrementAndGet(): Int
} }
private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) { private fun Point3D?.safePlus(other: Point3D?): Point3D? = if (this == null && other == null) {

View File

@ -1,8 +1,8 @@
package hep.dataforge.vision.gdml package hep.dataforge.vision.gdml
actual class Counter { public actual class Counter {
private var count: Int = 0 private var count: Int = 0
actual fun get(): Int = count public actual fun get(): Int = count
actual fun incrementAndGet(): Int = count++ public actual fun incrementAndGet(): Int = count++
} }

View File

@ -7,7 +7,7 @@ import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
actual typealias Counter = AtomicInteger public actual typealias Counter = AtomicInteger
fun GDML.Companion.readFile(file: Path): GDML { fun GDML.Companion.readFile(file: Path): GDML {
val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8") val xmlReader = StAXReader(Files.newInputStream(file), "UTF-8")

View File

@ -29,11 +29,11 @@ import info.laht.threekt.math.Vector2
import info.laht.threekt.objects.LineSegments import info.laht.threekt.objects.LineSegments
import info.laht.threekt.objects.Mesh import info.laht.threekt.objects.Mesh
import info.laht.threekt.scenes.Scene import info.laht.threekt.scenes.Scene
import kotlinx.browser.window
import kotlinx.dom.clear
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.Node import org.w3c.dom.Node
import org.w3c.dom.events.MouseEvent import org.w3c.dom.events.MouseEvent
import kotlin.browser.window
import kotlin.dom.clear
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.max import kotlin.math.max
import kotlin.math.sin import kotlin.math.sin
@ -41,11 +41,16 @@ import kotlin.math.sin
/** /**
* *
*/ */
class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Canvas3DOptions) : Renderer<Solid> { public class ThreeCanvas(
element: HTMLElement,
public val three: ThreePlugin,
public val options: Canvas3DOptions,
public val onClick: ((Name?) -> Unit)? = null,
) : Renderer<Solid> {
override val context: Context get() = three.context override val context: Context get() = three.context
var content: Solid? = null public var content: Solid? = null
private set private set
private var root: Object3D? = null private var root: Object3D? = null
@ -53,17 +58,15 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
private val raycaster = Raycaster() private val raycaster = Raycaster()
private val mousePosition: Vector2 = Vector2() private val mousePosition: Vector2 = Vector2()
var onClick: ((Name?) -> Unit)? = null public val axes: AxesHelper = AxesHelper(options.axes.size.toInt()).apply {
val axes = AxesHelper(options.axes.size.toInt()).apply {
visible = options.axes.visible visible = options.axes.visible
} }
val scene: Scene = Scene().apply { public val scene: Scene = Scene().apply {
add(axes) add(axes)
} }
val camera = buildCamera(options.camera) public val camera: PerspectiveCamera = buildCamera(options.camera)
private var picked: Object3D? = null private var picked: Object3D? = null
@ -166,7 +169,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
} }
} }
fun clear() { public fun clear() {
scene.children.find { it.name == "@root" }?.let { scene.children.find { it.name == "@root" }?.let {
scene.remove(it) scene.remove(it)
} }
@ -192,7 +195,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
private fun Object3D.toggleHighlight( private fun Object3D.toggleHighlight(
highlight: Boolean, highlight: Boolean,
edgesName: String, edgesName: String,
material: LineBasicMaterial = SELECTED_MATERIAL material: LineBasicMaterial = SELECTED_MATERIAL,
) { ) {
if (userData[DO_NOT_HIGHLIGHT_TAG] == true) { if (userData[DO_NOT_HIGHLIGHT_TAG] == true) {
return return
@ -220,7 +223,7 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
/** /**
* Toggle highlight for element with given name * Toggle highlight for element with given name
*/ */
fun select(name: Name?) { public fun select(name: Name?) {
if (name == null) { if (name == null) {
selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL) selected?.toggleHighlight(false, SELECT_NAME, SELECTED_MATERIAL)
selected = null selected = null
@ -234,15 +237,21 @@ class ThreeCanvas(element: HTMLElement, val three: ThreePlugin, val options: Can
} }
} }
companion object { public companion object {
const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight" public const val DO_NOT_HIGHLIGHT_TAG = "doNotHighlight"
private const val HIGHLIGHT_NAME = "@highlight" private const val HIGHLIGHT_NAME = "@highlight"
private const val SELECT_NAME = "@select" private const val SELECT_NAME = "@select"
} }
} }
fun ThreePlugin.output(element: HTMLElement, spec: Canvas3DOptions = Canvas3DOptions.empty()): ThreeCanvas = public fun ThreePlugin.output(
ThreeCanvas(element, this, spec) element: HTMLElement,
spec: Canvas3DOptions = Canvas3DOptions.empty(),
onClick: ((Name?) -> Unit)? = null,
): ThreeCanvas = ThreeCanvas(element, this, spec, onClick)
fun ThreePlugin.render(element: HTMLElement, obj: Solid, spec: Canvas3DOptions = Canvas3DOptions.empty()): Unit = public fun ThreePlugin.render(
output(element, spec).render(obj) element: HTMLElement, obj: Solid,
spec: Canvas3DOptions = Canvas3DOptions.empty(),
onClick: ((Name?) -> Unit)? = null,
): Unit = output(element, spec, onClick).render(obj)

View File

@ -6,67 +6,88 @@ import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.specifications.Canvas3DOptions import hep.dataforge.vision.solid.specifications.Canvas3DOptions
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import react.RBuilder import react.*
import react.RComponent
import react.RProps
import react.RState
import react.dom.div import react.dom.div
import react.dom.findDOMNode
public external interface ThreeCanvasProps : RProps { public external interface ThreeCanvasProps : RProps {
var context: Context public var context: Context
var obj: Solid public var obj: Solid?
var options: Canvas3DOptions? public var options: Canvas3DOptions?
var selected: Name? public var selected: Name?
var clickCallback: (Name?) -> Unit public var clickCallback: (Name?) -> Unit
var canvasCallback: ((ThreeCanvas?) -> Unit)? public var canvasCallback: ((ThreeCanvas?) -> Unit)?
} }
public external interface ThreeCanvasState : RState { public external interface ThreeCanvasState : RState {
var element: Element? public var element: Element?
// var canvas: ThreeCanvas? // var canvas: ThreeCanvas?
} }
@JsExport public val ThreeCanvasComponent: FunctionalComponent<ThreeCanvasProps> = functionalComponent(
public class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() { "ThreeCanvasComponent"
) { props ->
val elementRef = useRef<Element?>(null)
var canvas by useState<ThreeCanvas?>(null)
private var canvas: ThreeCanvas? = null useEffect(listOf(props.context, props.obj, props.options, elementRef)) {
if (canvas == null) {
override fun componentDidMount() { val element = elementRef.current as? HTMLElement ?: error("Canvas element not found")
if(canvas == null) {
val element = state.element as? HTMLElement ?: error("Canvas element not found")
val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin) val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
canvas = three.output(element, props.options ?: Canvas3DOptions.empty()).apply { val newCanvas = three.output(element, props.options ?: Canvas3DOptions.empty(), props.clickCallback)
onClick = props.clickCallback props.canvasCallback?.invoke(newCanvas)
} canvas = newCanvas
props.canvasCallback?.invoke(canvas)
}
canvas?.render(props.obj)
}
override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
if (prevProps.obj != props.obj) {
componentDidMount()
}
if (prevProps.selected != props.selected) {
canvas?.select(props.selected)
} }
} }
override fun RBuilder.render() { useEffect(listOf(canvas, props.obj)) {
div { props.obj?.let { obj ->
ref { if (canvas?.content != obj) {
state.element = findDOMNode(it) canvas?.render(obj)
} }
} }
} }
}
public fun RBuilder.threeCanvas(object3D: Solid, options: Canvas3DOptions.() -> Unit = {}) { useEffect(listOf(canvas, props.selected)) {
child(ThreeCanvasComponent::class) { canvas?.select(props.selected)
attrs { }
this.obj = object3D
this.options = Canvas3DOptions.invoke(options) div {
} ref = elementRef
} }
} }
//public class ThreeCanvasComponent : RComponent<ThreeCanvasProps, ThreeCanvasState>() {
//
// private var canvas: ThreeCanvas? = null
//
// override fun componentDidMount() {
// props.obj?.let { obj ->
// if (canvas == null) {
// val element = state.element as? HTMLElement ?: error("Canvas element not found")
// val three: ThreePlugin = props.context.plugins.fetch(ThreePlugin)
// canvas = three.output(element, props.options ?: Canvas3DOptions.empty()).apply {
// onClick = props.clickCallback
// }
// props.canvasCallback?.invoke(canvas)
// }
// canvas?.render(obj)
// }
// }
//
// override fun componentDidUpdate(prevProps: ThreeCanvasProps, prevState: ThreeCanvasState, snapshot: Any) {
// if (prevProps.obj != props.obj) {
// componentDidMount()
// }
// if (prevProps.selected != props.selected) {
// canvas?.select(props.selected)
// }
// }
//
// override fun RBuilder.render() {
// div {
// ref {
// state.element = findDOMNode(it)
// }
// }
// }
//}