diff --git a/kernelf-editor/src/jsMain/kotlin/KernelfApiJS.kt b/kernelf-editor/src/jsMain/kotlin/KernelfApiJS.kt index 9aee8b2f..c926ba6e 100644 --- a/kernelf-editor/src/jsMain/kotlin/KernelfApiJS.kt +++ b/kernelf-editor/src/jsMain/kotlin/KernelfApiJS.kt @@ -67,7 +67,7 @@ object KernelfApiJS { private var updateScheduled = atomic(false) private val coroutinesScope = CoroutineScope(Dispatchers.Main) override fun treeChanged(oldTree: ITree?, newTree: ITree) { - if (editor.getHtmlElement().unwrap().isInDocument()) { + if (editor.containerElement.unwrap().isInDocument()) { if (!updateScheduled.getAndSet(true)) { coroutinesScope.launch { updateScheduled.getAndSet(false) @@ -87,7 +87,7 @@ object KernelfApiJS { private var updateScheduled = atomic(false) private val coroutinesScope = CoroutineScope(Dispatchers.Main) override fun areaChanged(changes: IAreaChangeList) { - if (editor.getHtmlElement().unwrap().isInDocument()) { + if (editor.containerElement.unwrap().isInDocument()) { if (!updateScheduled.getAndSet(true)) { coroutinesScope.launch { updateScheduled.getAndSet(false) @@ -104,7 +104,7 @@ object KernelfApiJS { }) } editor.updateHtml() - return editor.getHtmlElement().unwrap() + return editor.containerElement.unwrap() } } diff --git a/projectional-editor-ssr-client/src/jsMain/kotlin/org/modelix/editor/ssr/client/ModelixSSRClient.kt b/projectional-editor-ssr-client/src/jsMain/kotlin/org/modelix/editor/ssr/client/ModelixSSRClient.kt index 9bd6f81c..6087da80 100644 --- a/projectional-editor-ssr-client/src/jsMain/kotlin/org/modelix/editor/ssr/client/ModelixSSRClient.kt +++ b/projectional-editor-ssr-client/src/jsMain/kotlin/org/modelix/editor/ssr/client/ModelixSSRClient.kt @@ -17,6 +17,7 @@ import kotlinx.html.js.div import kotlinx.html.onClick import kotlinx.html.tabIndex import launchLogging +import org.modelix.editor.JSKeyboardEventType import org.modelix.editor.JSMouseEventType import org.modelix.editor.convert import org.modelix.editor.getAbsoluteBounds @@ -109,7 +110,7 @@ class ModelixSSRClient(private val httpClient: HttpClient, private val url: Stri private inner class EditorSession(val editorId: String, rootNodeReference: INodeReference) { val containerElement: HTMLDivElement = document.create.div("modelix-text-editor-component") { - tabIndex = "-1" + tabIndex = "-1" // allows setting keyboard focus } val editorElement: HTMLDivElement = containerElement.append.div { id = editorId @@ -127,6 +128,20 @@ class ModelixSSRClient(private val httpClient: HttpClient, private val url: Stri mouseEvent = event.convert(JSMouseEventType.CLICK, containerElement) ).withBounds().send() } + containerElement.onkeydown = { event -> + MessageFromClient( + editorId = editorId, + keyboardEvent = event.convert(JSKeyboardEventType.KEYDOWN) + ).withBounds().send() + event.preventDefault() + } + containerElement.onkeyup = { event -> + MessageFromClient( + editorId = editorId, + keyboardEvent = event.convert(JSKeyboardEventType.KEYUP) + ).withBounds().send() + event.preventDefault() + } } fun computeBoundsUpdate(): Map? { diff --git a/projectional-editor-ssr-server/src/main/kotlin/org/modelix/editor/ssr/server/ModelixSSRServer.kt b/projectional-editor-ssr-server/src/main/kotlin/org/modelix/editor/ssr/server/ModelixSSRServer.kt index 2ad436df..5f503437 100644 --- a/projectional-editor-ssr-server/src/main/kotlin/org/modelix/editor/ssr/server/ModelixSSRServer.kt +++ b/projectional-editor-ssr-server/src/main/kotlin/org/modelix/editor/ssr/server/ModelixSSRServer.kt @@ -131,7 +131,7 @@ class ModelixSSRServer(private val nodeResolutionScope: INodeResolutionScope) { fun sendUpdate() { LOG.debug { "($editorId) sendUpdate" } editorComponent!!.update() - val dom = editorComponent!!.getHtmlElement() + val dom = editorComponent!!.getHtmlElement()!! LOG.debug { "($editorId) dom: $dom" } val lastestDomState = HashMap() var rootData = toUpdateData(dom, lastestDomState) diff --git a/projectional-editor/src/commonMain/kotlin/org/modelix/editor/EditorComponent.kt b/projectional-editor/src/commonMain/kotlin/org/modelix/editor/EditorComponent.kt index a301e9c2..4584c3f6 100644 --- a/projectional-editor/src/commonMain/kotlin/org/modelix/editor/EditorComponent.kt +++ b/projectional-editor/src/commonMain/kotlin/org/modelix/editor/EditorComponent.kt @@ -2,7 +2,6 @@ package org.modelix.editor import kotlinx.html.TagConsumer import kotlinx.html.div -import kotlinx.html.tabIndex import org.modelix.incremental.IncrementalIndex import kotlin.math.abs import kotlin.math.min @@ -26,13 +25,10 @@ open class EditorComponent( } private var selectionView: SelectionView<*>? = null val generatedHtmlMap = GeneratedHtmlMap() - protected var containerElement: IVirtualDom.HTMLElement = virtualDom.create().div("js-editor-component") { - tabIndex = "-1" // allows setting keyboard focus - } private var highlightedLine: IVirtualDom.HTMLElement? = null private var highlightedCell: IVirtualDom.HTMLElement? = null fun getMainLayer(): IVirtualDom.HTMLElement? { - return containerElement.descendants().filterIsInstance().find { it.getClasses().contains(MAIN_LAYER_CLASS_NAME) } + return getHtmlElement()?.childNodes?.filterIsInstance()?.find { it.getClasses().contains(MAIN_LAYER_CLASS_NAME) } } fun selectAfterUpdate(newSelection: () -> Selection?) { @@ -45,7 +41,7 @@ open class EditorComponent( override fun isHtmlOutputValid(): Boolean = false - fun getHtmlElement(): IVirtualDom.HTMLElement = containerElement + fun getHtmlElement(): IVirtualDom.HTMLElement? = generatedHtmlMap.getOutput(this) private fun updateRootCell() { val oldRootCell = rootCell @@ -68,12 +64,13 @@ open class EditorComponent( codeCompletionMenu?.let { CodeCompletionMenuUI(it, this).updateBounds() } } + open protected fun editorElementChanged(newElement: IVirtualDom.HTMLElement) {} + fun updateHtml() { val oldEditorElement = generatedHtmlMap.getOutput(this) val newEditorElement = IncrementalVirtualDOMBuilder(virtualDom, oldEditorElement, generatedHtmlMap).produce(this)() if (newEditorElement != oldEditorElement) { - oldEditorElement?.remove() - containerElement.appendChild(newEditorElement) + editorElementChanged(newEditorElement) } val selectedLayoutable = (getSelection() as? CaretSelection)?.layoutable diff --git a/projectional-editor/src/jsMain/kotlin/org/modelix/editor/JsEditorComponent.kt b/projectional-editor/src/jsMain/kotlin/org/modelix/editor/JsEditorComponent.kt index 3aa8a40f..0c78377c 100644 --- a/projectional-editor/src/jsMain/kotlin/org/modelix/editor/JsEditorComponent.kt +++ b/projectional-editor/src/jsMain/kotlin/org/modelix/editor/JsEditorComponent.kt @@ -1,20 +1,33 @@ package org.modelix.editor +import kotlinx.html.div +import kotlinx.html.tabIndex import org.w3c.dom.events.Event import org.w3c.dom.events.KeyboardEvent import org.w3c.dom.events.MouseEvent class JsEditorComponent(engine: EditorEngine, rootCellCreator: (EditorState) -> Cell) : EditorComponent(engine, JSDom(), rootCellCreator), IProducesHtml { + + val containerElement: IVirtualDom.HTMLElement = virtualDom.create().div("js-editor-component") { + tabIndex = "-1" // allows setting keyboard focus + } + init { - (virtualDom as JSDom).originElement = getHtmlElement().unwrap() - getHtmlElement().unwrap().addEventListener("click", { event: Event -> - (event as? MouseEvent)?.let { processMouseEvent(it.convert(JSMouseEventType.CLICK, getHtmlElement().unwrap())) } + (virtualDom as JSDom).originElement = containerElement.unwrap() + containerElement.unwrap().addEventListener("click", { event: Event -> + (event as? MouseEvent)?.let { processMouseEvent(it.convert(JSMouseEventType.CLICK, containerElement.unwrap())) } }) - getHtmlElement().unwrap().addEventListener("keydown", { event: Event -> + containerElement.unwrap().addEventListener("keydown", { event: Event -> (event as? KeyboardEvent)?.let { if (processKeyDown(it.convert(JSKeyboardEventType.KEYDOWN))) event.preventDefault() } }) - getHtmlElement().unwrap().addEventListener("keyup", { event: Event -> + containerElement.unwrap().addEventListener("keyup", { event: Event -> (event as? KeyboardEvent)?.let { if (processKeyDown(it.convert(JSKeyboardEventType.KEYUP))) event.preventDefault() } }) } + + override fun editorElementChanged(newElement: IVirtualDom.HTMLElement) { + super.editorElementChanged(newElement) + containerElement.childNodes.forEach { it.remove() } + containerElement.appendChild(newElement) + } }