diff --git a/.github/workflows/publish-to-gh-pages.yml b/.github/workflows/publish-to-gh-pages.yml index 4fd22b5..c3c89bb 100644 --- a/.github/workflows/publish-to-gh-pages.yml +++ b/.github/workflows/publish-to-gh-pages.yml @@ -32,7 +32,7 @@ jobs: - name: Upload build reports uses: actions/upload-pages-artifact@v1 with: - path: build/distributions/ + path: build/dist/js/productionExecutable/ # Deploy job deploy: # The type of runner that the job will run on diff --git a/README.md b/README.md index faa86ed..b1e2604 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ JVB dashboard provides a UI to connect to a JVB's REST API and display live grap * Run `./gradlew build` # Deploying -The built files will be in `/build/distributions`. These files can be copied anywhere and hosted. The easiest way to host -them locally is to run `python3 -m http.server` in the `/build/distributions` directory. +The built files will be in `/build/dist/js/productionExecutable`. These files can be copied anywhere and hosted. The easiest way to host +them locally is to run `python3 -m http.server` in the `/build/dist/js/productionExecutable` directory. # Known issues * The JVB's REST API is usually not publicly accessible, so the easiest way to have the UI connect to it is to create an diff --git a/build.gradle.kts b/build.gradle.kts index 05c20e1..d915d36 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.jetbrains.kotlin.js") version "1.4.10" + kotlin("multiplatform") version "2.0.0" } group = "org.jitsi" @@ -7,53 +7,50 @@ version = "1.0-SNAPSHOT" repositories { mavenCentral() - jcenter() -} - -dependencies { - implementation(kotlin("stdlib-js")) - - implementation("org.jetbrains:kotlin-react:16.13.1-pre.110-kotlin-1.4.0") - implementation("org.jetbrains:kotlin-react-dom:16.13.1-pre.110-kotlin-1.4.0") - implementation(npm("react", "16.13.1")) - implementation(npm("react-dom", "16.13.1")) - - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") - - implementation(npm("highcharts", "8.2.2")) - implementation(npm("highcharts-react-official", "3.0.0")) - - implementation("org.jetbrains:kotlin-styled:1.0.0-pre.110-kotlin-1.4.0") - implementation(npm("styled-components", "~5.1.1")) - implementation(npm("inline-style-prefixer", "~6.0.0")) - implementation(npm("react-select", "~3.1.0")) - implementation("org.jetbrains:kotlin-react-router-dom:5.2.0-pre.136-kotlin-1.4.10") } kotlin { - sourceSets.configureEach { - languageSettings.apply { - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlin.time.ExperimentalTime") - } - } js { browser { webpackTask { - cssSupport.enabled = true + cssSupport { + enabled.set(true) + } } runTask { - cssSupport.enabled = true - } - - testTask { - useKarma { - useChromeHeadless() - webpackConfig.cssSupport.enabled = true + cssSupport { + enabled.set(true) } } } binaries.executable() } + sourceSets["jsMain"].languageSettings { + apply { + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlin.time.ExperimentalTime") + } + } + + sourceSets["jsMain"].dependencies { + implementation(kotlin("stdlib-js")) + + implementation("org.jetbrains.kotlin-wrappers:kotlin-react:18.3.1-pre.770") + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-legacy:18.3.1-pre.770") + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom-legacy:18.3.1-pre.770") + + implementation(npm("react", "18.3.1")) + implementation(npm("react-dom", "18.3.1")) + + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") + + implementation(npm("highcharts", "11.4.6")) + implementation(npm("highcharts-react-official", "3.2.1")) + + implementation("org.jetbrains.kotlin-wrappers:kotlin-styled-next:1.2.4-pre.770") + implementation(npm("inline-style-prefixer", "~7.0.1")) + implementation(npm("react-select", "~5.8.0")) + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-router-dom:6.23.1-pre.770") + } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c05..490fda8 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4b4429..fae0804 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index fbd7c51..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -82,7 +82,6 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -130,7 +129,6 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index a9f778a..9109989 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -84,7 +84,6 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% diff --git a/src/jsMain/kotlin/App.kt b/src/jsMain/kotlin/App.kt new file mode 100644 index 0000000..a128314 --- /dev/null +++ b/src/jsMain/kotlin/App.kt @@ -0,0 +1,53 @@ +import js.objects.jso +import react.FC +import react.Props +import react.RBuilder +import react.RComponent +import react.PropsWithChildren +import react.State +import react.dom.html.ReactHTML.div +import react.dom.html.ReactHTML.span +import react.react +import react.router.RouterProvider +import react.router.dom.Link +import react.router.dom.createHashRouter + +val root = FC { + div { + Link { + to = "/dump" + +"Dump viewer" + } + span { + +" | " + } + Link { + to = "/live" + +"Live dashboard" + } + } +} + + +private val hashRouter = createHashRouter( + routes = arrayOf( + jso { + path = "/" + Component = root + }, + jso { + path = "/dump" + Component = Dump::class.react + }, + jso { + path = "/live" + Component = LiveDashboard::class.react + } + ) +) + +val App = FC { + RouterProvider { + router = hashRouter + } +} diff --git a/src/main/kotlin/ChartSelection.kt b/src/jsMain/kotlin/ChartSelection.kt similarity index 90% rename from src/main/kotlin/ChartSelection.kt rename to src/jsMain/kotlin/ChartSelection.kt index 85ef570..aeaaf7b 100644 --- a/src/main/kotlin/ChartSelection.kt +++ b/src/jsMain/kotlin/ChartSelection.kt @@ -3,11 +3,12 @@ import highcharts.Event import highcharts.Point import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State +import react.RefCallback // Defines a selector and the graph itself, and acts as the 'go between' between the two -class ChartSelection : RComponent() { +class ChartSelection : RComponent() { private var currentlyGraphedKeys = listOf() private var selector: Selector? = null private var chart: Chart? = null @@ -52,8 +53,8 @@ class ChartSelection : RComponent() { onSelectedKeysChange = this@ChartSelection::selectedKeysChanged allKeys = props.allKeys } - ref { - selector = it as? Selector + ref = RefCallback { + selector = it } } child(Chart::class) { @@ -63,8 +64,8 @@ class ChartSelection : RComponent() { graphType = props.graphType ?: "line" startZoomSeconds = props.startZoomSeconds } - ref { - chart = it as? Chart + ref = RefCallback { + chart = it } } } @@ -97,7 +98,7 @@ class ChartSelection : RComponent() { // Leaving this here, but since it doesn't propagate these values to props, it's a bit cumbersome to use. // See https://github.com/JetBrains/kotlin-wrappers/issues/385 -// companion object : RStatics(GraphSelection::class) { +// companion object : RStatics(GraphSelection::class) { // init { // defaultProps = GraphSelectionProps().apply { // allKeys = emptyList() @@ -107,7 +108,7 @@ class ChartSelection : RComponent() { // } } -external interface GraphSelectionProps : RProps { +external interface GraphSelectionProps : PropsWithChildren { var title: String? var allKeys: List? // An optional property which can contain stored data to be graphed diff --git a/src/main/kotlin/ChartZoomButtons.kt b/src/jsMain/kotlin/ChartZoomButtons.kt similarity index 70% rename from src/main/kotlin/ChartZoomButtons.kt rename to src/jsMain/kotlin/ChartZoomButtons.kt index 83aac0c..70d32ce 100644 --- a/src/main/kotlin/ChartZoomButtons.kt +++ b/src/jsMain/kotlin/ChartZoomButtons.kt @@ -3,13 +3,14 @@ import kotlinx.css.fontWeight import kotlinx.html.js.onClickFunction import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.setState import styled.css import styled.styledButton import kotlin.time.Duration + class ChartZoomButtons : RComponent() { init { // Default to 1 minute @@ -25,14 +26,12 @@ class ChartZoomButtons : RComponent() { this.fontWeight = FontWeight.bold } } - attrs { - text(buttonDesc.title) - onClickFunction = { - setState { - currZoomSeconds = buttonDesc.zoomSeconds - } - props.onZoomChange?.invoke(buttonDesc.zoomSeconds) + attrs.text(buttonDesc.title) + attrs.onClickFunction = { + setState { + currZoomSeconds = buttonDesc.zoomSeconds } + props.onZoomChange?.invoke(buttonDesc.zoomSeconds) } } } @@ -41,11 +40,11 @@ class ChartZoomButtons : RComponent() { fun currZoomSeconds(): Int = state.currZoomSeconds } -external interface ChartZoomState : RState { +external interface ChartZoomState : State { var currZoomSeconds: Int } -external interface ChartZoomProps : RProps { +external interface ChartZoomProps : PropsWithChildren { var onZoomChange: ((Int) -> Unit)? var buttons: List? } @@ -57,4 +56,4 @@ data class ZoomButtonDesc( ) fun ZoomButtonDesc(title: String, size: Duration): ZoomButtonDesc = - ZoomButtonDesc(title, size.inSeconds.toInt()) + ZoomButtonDesc(title, size.inWholeSeconds.toInt()) diff --git a/src/main/kotlin/Conference.kt b/src/jsMain/kotlin/Conference.kt similarity index 94% rename from src/main/kotlin/Conference.kt rename to src/jsMain/kotlin/Conference.kt index beb5e99..60ee09a 100644 --- a/src/main/kotlin/Conference.kt +++ b/src/jsMain/kotlin/Conference.kt @@ -6,7 +6,6 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.await import kotlinx.coroutines.cancel -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -16,8 +15,9 @@ import kotlinx.css.pct import kotlinx.html.js.onClickFunction import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.RefCallback +import react.State import react.dom.div import react.dom.h2 import react.dom.p @@ -141,11 +141,9 @@ class Conference : RComponent() { } else { +"Conference (${props.id})" } - attrs { - onClickFunction = { _ -> - setState { - expanded = !expanded - } + attrs.onClickFunction = { _ -> + setState { + expanded = !expanded } } } @@ -168,9 +166,9 @@ class Conference : RComponent() { nonNumericalKeys = state.nonNumericalKeys data = props.confData } - ref { + ref = RefCallback { if (it != null) { - chartCollection = it as ChartCollection + chartCollection = it } } } @@ -191,9 +189,9 @@ class Conference : RComponent() { data = existingEpData } } - ref { + ref = RefCallback { if (it != null) { - eps[epId] = it as Endpoint + eps[epId] = it } } } @@ -215,9 +213,9 @@ class Conference : RComponent() { data = existingEpData } } - ref { + ref = RefCallback { if (it != null) { - eps[relayId] = it as Endpoint + eps[relayId] = it } } } @@ -283,7 +281,7 @@ private fun getRelayIds(confData: dynamic): Array { keys(confData.relays) else emptyArray() } -external interface ConferenceState : RState { +external interface ConferenceState : State { var epIds: Array var relayIds: Array var name: String @@ -293,7 +291,7 @@ external interface ConferenceState : RState { var dataByEp: MutableMap>? } -external interface ConferenceProps : RProps { +external interface ConferenceProps : PropsWithChildren { var baseRestApiUrl: String? var id: String var confData: List? diff --git a/src/main/kotlin/Dump.kt b/src/jsMain/kotlin/Dump.kt similarity index 70% rename from src/main/kotlin/Dump.kt rename to src/jsMain/kotlin/Dump.kt index c99f81e..58ba71f 100644 --- a/src/main/kotlin/Dump.kt +++ b/src/jsMain/kotlin/Dump.kt @@ -1,16 +1,15 @@ -import kotlinx.html.InputType -import kotlinx.html.js.onChangeFunction import org.w3c.dom.HTMLInputElement import org.w3c.files.File import react.RBuilder import react.RComponent -import react.RProps -import react.RState -import react.dom.div -import react.dom.input +import react.PropsWithChildren +import react.State +import react.dom.html.ReactHTML.div +import react.dom.html.ReactHTML.input import react.setState +import web.html.InputType -class Dump : RComponent() { +class Dump : RComponent() { init { state.file = null } @@ -18,9 +17,10 @@ class Dump : RComponent() { console.log("blah") if (state.file == null) { div { - input(type = InputType.file) { + input { attrs { - onChangeFunction = { event -> + type = InputType.file + onChange = { event -> val file = (event.target as HTMLInputElement).files?.item(0) console.log("got file ", file) setState { @@ -40,6 +40,6 @@ class Dump : RComponent() { } } -external interface DumpState : RState { +external interface DumpState : State { var file: File? } diff --git a/src/main/kotlin/DumpViewer.kt b/src/jsMain/kotlin/DumpViewer.kt similarity index 96% rename from src/main/kotlin/DumpViewer.kt rename to src/jsMain/kotlin/DumpViewer.kt index 217b095..7348851 100644 --- a/src/main/kotlin/DumpViewer.kt +++ b/src/jsMain/kotlin/DumpViewer.kt @@ -2,8 +2,8 @@ import org.w3c.files.File import org.w3c.files.FileReader import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.setState class DumpViewer : RComponent() { @@ -103,12 +103,12 @@ private fun getConfName(data: List?): String { ?.firstOrNull { it != undefined } as? String ?: "No conf name found" } -external interface DumpViewerState : RState { +external interface DumpViewerState : State { // A list of JSON stat entries from the dump var data: List? var error: String? } -external interface DumpViewerProps : RProps { +external interface DumpViewerProps : PropsWithChildren { var file: File } diff --git a/src/main/kotlin/Endpoint.kt b/src/jsMain/kotlin/Endpoint.kt similarity index 93% rename from src/main/kotlin/Endpoint.kt rename to src/jsMain/kotlin/Endpoint.kt index c47851e..f6a6a2a 100644 --- a/src/main/kotlin/Endpoint.kt +++ b/src/jsMain/kotlin/Endpoint.kt @@ -4,8 +4,9 @@ import kotlinx.css.paddingTop import kotlinx.css.pct import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.RefCallback +import react.State import react.dom.div import react.dom.h3 import react.setState @@ -97,9 +98,9 @@ class Endpoint : RComponent() { nonNumericalKeys = state.nonNumericalKeys data = props.data } - ref { + ref = RefCallback { if (it != null) { - chartCollection = it as ChartCollection + chartCollection = it } } } @@ -110,7 +111,7 @@ class Endpoint : RComponent() { private fun usingLiveData(): Boolean = props.baseRestApiUrl != null } -external interface EpProps : RProps { +external interface EpProps : PropsWithChildren { var entityType: String var confId: String var id: String @@ -121,7 +122,7 @@ external interface EpProps : RProps { // Endpoints don't retrieve their own data, the conference makes a single // request and updates the props of the ep components -external interface EpState : RState { +external interface EpState : State { var numericalKeys: List var nonNumericalKeys: List var statsId: String? diff --git a/src/main/kotlin/FeatureToggle.kt b/src/jsMain/kotlin/FeatureToggle.kt similarity index 72% rename from src/main/kotlin/FeatureToggle.kt rename to src/jsMain/kotlin/FeatureToggle.kt index 8e29a48..4d1da3a 100644 --- a/src/main/kotlin/FeatureToggle.kt +++ b/src/jsMain/kotlin/FeatureToggle.kt @@ -8,14 +8,16 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.css.Display +import kotlinx.css.Margin import kotlinx.css.display import kotlinx.css.margin +import kotlinx.css.px import kotlinx.html.js.onClickFunction import org.w3c.xhr.XMLHttpRequest import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.dom.button import react.dom.div import react.setState @@ -54,22 +56,20 @@ class FeatureToggle : RComponent() { styledDiv { css { display = Display.inlineBlock - margin = "10px" + margin = Margin(10.px) } +props.featureName div { button { val isEnabled: Boolean = state.enabled - attrs { - text(if (isEnabled) "Disable" else "Enable") - onClickFunction = { _ -> - XMLHttpRequest().apply { - open("POST", "${props.url}/${!isEnabled}", async = true) - send() - } - setState { - enabled = !state.enabled - } + attrs.text(if (isEnabled) "Disable" else "Enable") + attrs.onClickFunction = { _ -> + XMLHttpRequest().apply { + open("POST", "${props.url}/${!isEnabled}", async = true) + send() + } + setState { + enabled = !state.enabled } } } @@ -78,11 +78,11 @@ class FeatureToggle : RComponent() { } } -external interface FeatureToggleState : RState { +external interface FeatureToggleState : State { var enabled: Boolean } -external interface FeatureToggleProps : RProps { +external interface FeatureToggleProps : PropsWithChildren { var featureName: String var url: String } diff --git a/src/main/kotlin/JicofoConference.kt b/src/jsMain/kotlin/JicofoConference.kt similarity index 94% rename from src/main/kotlin/JicofoConference.kt rename to src/jsMain/kotlin/JicofoConference.kt index c718a10..effb9fd 100644 --- a/src/main/kotlin/JicofoConference.kt +++ b/src/jsMain/kotlin/JicofoConference.kt @@ -16,8 +16,9 @@ import kotlinx.css.pct import kotlinx.html.js.onClickFunction import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.RefCallback +import react.State import react.dom.div import react.dom.h2 import react.dom.p @@ -126,11 +127,9 @@ class JicofoConference : RComponent - setState { - expanded = !expanded - } + attrs.onClickFunction = { _ -> + setState { + expanded = !expanded } } } @@ -152,9 +151,9 @@ class JicofoConference : RComponent { if (it != null) { - chartCollection = it as ChartCollection + chartCollection = it } } } @@ -174,9 +173,9 @@ class JicofoConference : RComponent { if (it != null) { - eps[epId] = it as Endpoint + eps[epId] = it } } } @@ -235,7 +234,7 @@ private fun getEpIds(confData: dynamic): Array { keys(confData.participants) else emptyArray() } -external interface JicofoConferenceState : RState { +external interface JicofoConferenceState : State { var epIds: Array var name: String var expanded: Boolean @@ -244,7 +243,7 @@ external interface JicofoConferenceState : RState { var dataByEp: MutableMap>? } -external interface JicofoConferenceProps : RProps { +external interface JicofoConferenceProps : PropsWithChildren { var baseRestApiUrl: String? var id: String var confData: List? diff --git a/src/main/kotlin/Jvb.kt b/src/jsMain/kotlin/Jvb.kt similarity index 96% rename from src/main/kotlin/Jvb.kt rename to src/jsMain/kotlin/Jvb.kt index d994a79..bbaae9e 100644 --- a/src/main/kotlin/Jvb.kt +++ b/src/jsMain/kotlin/Jvb.kt @@ -12,8 +12,8 @@ import org.w3c.fetch.RequestInit import org.w3c.fetch.RequestMode import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.dom.div import react.dom.hr import react.dom.p @@ -128,12 +128,12 @@ class Jvb : RComponent() { } } -external interface JvbState : RState { +external interface JvbState : State { var state: dynamic var error: String? } -external interface JvbProps : RProps { +external interface JvbProps : PropsWithChildren { var url: String var updateIntervalMs: Long? } diff --git a/src/main/kotlin/LiveDashboard.kt b/src/jsMain/kotlin/LiveDashboard.kt similarity index 56% rename from src/main/kotlin/LiveDashboard.kt rename to src/jsMain/kotlin/LiveDashboard.kt index 79da6ce..5342330 100644 --- a/src/main/kotlin/LiveDashboard.kt +++ b/src/jsMain/kotlin/LiveDashboard.kt @@ -5,8 +5,8 @@ import kotlinx.html.js.onClickFunction import org.w3c.dom.HTMLInputElement import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.dom.button import react.dom.defaultValue import react.dom.h1 @@ -14,7 +14,7 @@ import react.dom.input import react.dom.p import react.setState -class LiveDashboard : RComponent() { +class LiveDashboard : RComponent() { init { state.jvbUrl = null } @@ -25,20 +25,16 @@ class LiveDashboard : RComponent() { p { key = "jvbUrl" input(type = InputType.text) { - attrs { - id = "jvb-url" - defaultValue = "127.0.0.1:4443" - } + attrs.id = "jvb-url" + attrs.defaultValue = "127.0.0.1:4443" } button { - attrs { - text("Set JVB") - onClickFunction = { _ -> - val inputUrl = (document.getElementById("jvb-url") as HTMLInputElement).value - console.log("setting jvb url to http://$inputUrl/debug") - setState { - jvbUrl = "http://$inputUrl/debug" - } + attrs.text("Set JVB") + attrs.onClickFunction = { _ -> + val inputUrl = (document.getElementById("jvb-url") as HTMLInputElement).value + console.log("setting jvb url to http://$inputUrl/debug") + setState { + jvbUrl = "http://$inputUrl/debug" } } } @@ -53,6 +49,6 @@ class LiveDashboard : RComponent() { } } -external interface AppState : RState { +external interface AppState : State { var jvbUrl: String? } diff --git a/src/main/kotlin/Selector.kt b/src/jsMain/kotlin/Selector.kt similarity index 89% rename from src/main/kotlin/Selector.kt rename to src/jsMain/kotlin/Selector.kt index b88dd11..7a99269 100644 --- a/src/main/kotlin/Selector.kt +++ b/src/jsMain/kotlin/Selector.kt @@ -1,11 +1,11 @@ import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import reactselect.AsyncSelect import reactselect.Option -class Selector : RComponent() { +class Selector : RComponent() { override fun RBuilder.render() { val allOptions = props.allKeys?.map { Option(it) }?.toTypedArray() ?: emptyArray() AsyncSelect { @@ -36,7 +36,7 @@ class Selector : RComponent() { } } -external interface SelectorProps : RProps { +external interface SelectorProps : PropsWithChildren { var allKeys: List? var onSelectedKeysChange: ((List) -> Unit)? } diff --git a/src/main/kotlin/StatusBadge.kt b/src/jsMain/kotlin/StatusBadge.kt similarity index 87% rename from src/main/kotlin/StatusBadge.kt rename to src/jsMain/kotlin/StatusBadge.kt index 203f8fa..17f606f 100644 --- a/src/main/kotlin/StatusBadge.kt +++ b/src/jsMain/kotlin/StatusBadge.kt @@ -2,8 +2,8 @@ import kotlinx.css.Color import kotlinx.css.color import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import styled.css import styled.styledDiv @@ -14,7 +14,7 @@ import styled.styledDiv abstract class StatusBadge( private val fieldName: String, private val evaluationFunc: (T) -> Status -) : RComponent, RState>() { +) : RComponent, State>() { override fun RBuilder.render() { val status = evaluationFunc(props.value) styledDiv { @@ -32,7 +32,7 @@ enum class Status(val color: Color) { RED(Color.red) } -external interface StatusBadgeProps : RProps { +external interface StatusBadgeProps : PropsWithChildren { var value: T } diff --git a/src/main/kotlin/Util.kt b/src/jsMain/kotlin/Util.kt similarity index 100% rename from src/main/kotlin/Util.kt rename to src/jsMain/kotlin/Util.kt diff --git a/src/main/kotlin/graphs/Chart.kt b/src/jsMain/kotlin/graphs/Chart.kt similarity index 93% rename from src/main/kotlin/graphs/Chart.kt rename to src/jsMain/kotlin/graphs/Chart.kt index 1583694..9069ecd 100644 --- a/src/main/kotlin/graphs/Chart.kt +++ b/src/jsMain/kotlin/graphs/Chart.kt @@ -15,14 +15,15 @@ import highcharts.XAxis import highcharts.highcharts import react.RBuilder import react.RComponent -import react.RProps -import react.RState +import react.PropsWithChildren +import react.State import react.ReactElement import react.dom.div +import react.RefCallback import kotlin.js.Date -class Chart : RComponent() { - private var chartRef: ReactElement? = null +class Chart : RComponent() { + private var chartRef: ReactElement? = null private val maxPoints: Int = 60 * 60 // How many seconds worth of live data we're currently displaying private var currentTimeZoomSeconds: Long = maxPoints.toLong() @@ -62,8 +63,8 @@ class Chart : RComponent() { attrs.highcharts = highcharts attrs.options = options attrs.allowChartUpdate = true - ref { - chartRef = it.unsafeCast() + ref = RefCallback> { + chartRef = it } } } @@ -135,7 +136,7 @@ class Chart : RComponent() { } } -external interface GraphProps : RProps { +external interface GraphProps : PropsWithChildren { var title: String? var enableZoom: Boolean var graphType: String? diff --git a/src/main/kotlin/graphs/ChartCollection.kt b/src/jsMain/kotlin/graphs/ChartCollection.kt similarity index 92% rename from src/main/kotlin/graphs/ChartCollection.kt rename to src/jsMain/kotlin/graphs/ChartCollection.kt index 5619148..00c75b4 100644 --- a/src/main/kotlin/graphs/ChartCollection.kt +++ b/src/jsMain/kotlin/graphs/ChartCollection.kt @@ -14,8 +14,8 @@ import react.dom.button import react.dom.div import styled.css import styled.styledDiv -import kotlin.time.minutes -import kotlin.time.seconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds class ChartCollection : RComponent() { private var chartSelectors: MutableMap = mutableMapOf() @@ -78,8 +78,8 @@ class ChartCollection : RComponent() ZoomButtonDesc("All", Int.MAX_VALUE.seconds), ) } - ref { - zoomButtons = it as? ChartZoomButtons + ref = RefCallback { + zoomButtons = it } } } @@ -111,9 +111,9 @@ class ChartCollection : RComponent() // yet startZoomSeconds = zoomButtons?.currZoomSeconds() ?: 60 } - ref { + ref = RefCallback { if (it != null) { - chartSelectors[chart.id] = it as ChartSelection + chartSelectors[chart.id] = it } } } @@ -131,9 +131,9 @@ class ChartCollection : RComponent() // yet startZoomSeconds = zoomButtons?.currZoomSeconds() ?: 60 } - ref { + ref = RefCallback { if (it != null) { - chartSelectors[chart.id] = it as ChartSelection + chartSelectors[chart.id] = it } } } @@ -148,11 +148,11 @@ class ChartCollection : RComponent() private fun usingLiveData(): Boolean = props.data == null } -external interface ChartCollectionState : RState { +external interface ChartCollectionState : State { var chartInfos: List } -external interface ChartCollectionProps : RProps { +external interface ChartCollectionProps : PropsWithChildren { var numericalKeys: List var nonNumericalKeys: List // An optional property to pass pre-existing data (e.g. from a dump file) diff --git a/src/main/kotlin/highcharts/Highcharts.kt b/src/jsMain/kotlin/highcharts/Highcharts.kt similarity index 95% rename from src/main/kotlin/highcharts/Highcharts.kt rename to src/jsMain/kotlin/highcharts/Highcharts.kt index 1f47d68..116c1ee 100644 --- a/src/main/kotlin/highcharts/Highcharts.kt +++ b/src/jsMain/kotlin/highcharts/Highcharts.kt @@ -4,12 +4,16 @@ import jsObject import org.w3c.dom.events.Event data class Title( + @JsName("text") val text: String ) data class XAxis( + @JsName("type") val type: String, /* "category" | "datetime" | "linear" | "logarithmic" | "treegrid" */ + @JsName("visible") val visible: Boolean = true, + @JsName("gridLineWidth") val gridLineWidth: Int = 1 ) @@ -20,7 +24,9 @@ external interface Point { var description: String? get() = definedExternally set(value) = definedExternally + @JsName("x") var x: Number + @JsName("y") var y: Number } @@ -153,6 +159,12 @@ external interface PlotSeriesOptions { var step: String? get() = definedExternally set(value) = definedExternally + var boostThreshold: Number? + get() = definedExternally + set(value) = definedExternally + var turboThreshold: Number? + get() = definedExternally + set(value) = definedExternally } fun PlotSeriesOptions(): PlotSeriesOptions = js("{}") diff --git a/src/main/kotlin/highcharts/HighchartsReact.kt b/src/jsMain/kotlin/highcharts/HighchartsReact.kt similarity index 54% rename from src/main/kotlin/highcharts/HighchartsReact.kt rename to src/jsMain/kotlin/highcharts/HighchartsReact.kt index 123fcb2..bd28f54 100644 --- a/src/main/kotlin/highcharts/HighchartsReact.kt +++ b/src/jsMain/kotlin/highcharts/HighchartsReact.kt @@ -3,13 +3,13 @@ package highcharts -import react.RClass -import react.RProps +import react.ComponentClass +import react.PropsWithChildren @JsName("default") -external val HighchartsReact: RClass +external val HighchartsReact: ComponentClass -external interface HighchartsReactProps : RProps { +external interface HighchartsReactProps : PropsWithChildren { var highcharts: Highcharts // var options: dynamic var options: Options diff --git a/src/main/kotlin/highcharts/HighchartsTimeline.kt b/src/jsMain/kotlin/highcharts/HighchartsTimeline.kt similarity index 100% rename from src/main/kotlin/highcharts/HighchartsTimeline.kt rename to src/jsMain/kotlin/highcharts/HighchartsTimeline.kt diff --git a/src/jsMain/kotlin/main.kt b/src/jsMain/kotlin/main.kt new file mode 100644 index 0000000..bdc221a --- /dev/null +++ b/src/jsMain/kotlin/main.kt @@ -0,0 +1,11 @@ +import highcharts.TimelineSeries +import highcharts.highcharts +import react.create +import react.dom.client.createRoot +import web.dom.document + +fun main() { + TimelineSeries(highcharts) + val root = createRoot(document.getElementById("root")!!) + root.render(App.create()) +} diff --git a/src/main/kotlin/reactselect/AsyncReactSelect.kt b/src/jsMain/kotlin/reactselect/AsyncReactSelect.kt similarity index 63% rename from src/main/kotlin/reactselect/AsyncReactSelect.kt rename to src/jsMain/kotlin/reactselect/AsyncReactSelect.kt index 450aebd..405f71c 100644 --- a/src/main/kotlin/reactselect/AsyncReactSelect.kt +++ b/src/jsMain/kotlin/reactselect/AsyncReactSelect.kt @@ -3,13 +3,13 @@ package reactselect -import react.RClass -import react.RProps +import react.ComponentClass +import react.PropsWithChildren @JsName("default") -external val AsyncSelect: RClass +external val AsyncSelect: ComponentClass -external interface AsyncReactSelectProps : RProps { +external interface AsyncReactSelectProps : PropsWithChildren { var loadOptions: (String /* inputValue */, (Array