Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c543536d2d | |||
| efb05ae156 | |||
| ba4a8aa7c1 | |||
| d069bd6047 | |||
| 0268ddbca0 | |||
| eacf977a0e |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@@ -2,7 +2,7 @@ name: Gradle build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev, master ]
|
||||
branches: [ dev, main ]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -10,8 +10,8 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3.5.1
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'liberica'
|
||||
@@ -19,6 +19,16 @@ jobs:
|
||||
- name: Gradle Wrapper Validation
|
||||
uses: gradle/wrapper-validation-action@v1.0.4
|
||||
- name: Gradle Build
|
||||
uses: gradle/gradle-build-action@v2.4.2
|
||||
uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
arguments: test jvmTest
|
||||
arguments: test jvmTest
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v6
|
||||
if: ${{ !cancelled() }} # always run even if the previous step fails
|
||||
with:
|
||||
report_paths: '**/test-results/**/TEST-*.xml'
|
||||
annotate_only: true
|
||||
detailed_summary: true
|
||||
flaky_summary: true
|
||||
include_empty_in_summary: false
|
||||
skip_success_summary: true
|
||||
@@ -3,14 +3,18 @@
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- Plotly implementation and demo for Kotlin/Wasm
|
||||
- Wasm targets for plotly-kt and visionforge-core/solid
|
||||
|
||||
### Changed
|
||||
- Order of arguments in Plotly-kt for js plotDiv functions
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
- Plotly-kt js demo
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ val dataforgeVersion by extra("0.10.2")
|
||||
|
||||
allprojects {
|
||||
group = "space.kscience"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2-dev-1"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
||||
@@ -11,11 +11,14 @@ import kotlinx.html.style
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.events.Event
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.plotly.*
|
||||
import space.kscience.plotly.events.PlotlyEventListenerType
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceType
|
||||
import space.kscience.plotly.models.histogram
|
||||
import space.kscience.plotly.models.scatter
|
||||
import kotlin.random.Random
|
||||
|
||||
private fun onDomLoaded(block: (Event) -> Unit) {
|
||||
@@ -35,7 +38,7 @@ fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Histogram demo" }
|
||||
plotDiv {
|
||||
plotDiv(Global) {
|
||||
val rnd = Random(222)
|
||||
histogram {
|
||||
name = "Random data"
|
||||
@@ -79,7 +82,7 @@ fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Dynamic trace demo" }
|
||||
plotDiv {
|
||||
plotDiv(Global) {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
29
plotly-kt/examples/wasm-demo/build.gradle.kts
Normal file
29
plotly-kt/examples/wasm-demo/build.gradle.kts
Normal file
@@ -0,0 +1,29 @@
|
||||
@file:OptIn(ExperimentalWasmDsl::class)
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.kotlin.link")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
wasmJs {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
wasmJsMain{
|
||||
dependencies{
|
||||
implementation(projects.plotlyKt.plotlyKtCore)
|
||||
implementation(spclibs.kotlinx.coroutines.core)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
146
plotly-kt/examples/wasm-demo/src/wasmJsMain/kotlin/main.kt
Normal file
146
plotly-kt/examples/wasm-demo/src/wasmJsMain/kotlin/main.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package space.kscience.plotly.wasmjsdemo
|
||||
|
||||
|
||||
import kotlinx.browser.document
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.dom.append
|
||||
import kotlinx.html.h1
|
||||
import kotlinx.html.js.div
|
||||
import kotlinx.html.style
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.HTMLElement
|
||||
import org.w3c.dom.events.Event
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.plotly.*
|
||||
import space.kscience.plotly.models.ScatterMode
|
||||
import space.kscience.plotly.models.TraceType
|
||||
import space.kscience.plotly.models.histogram
|
||||
import space.kscience.plotly.models.scatter
|
||||
import kotlin.random.Random
|
||||
|
||||
private fun onDomLoaded(block: (Event) -> Unit) {
|
||||
document.addEventListener("DOMContentLoaded", block)
|
||||
}
|
||||
|
||||
private fun withCanvas(block: TagConsumer<Element>.() -> Unit) {
|
||||
val element = document.getElementById("canvas") as? HTMLElement
|
||||
?: error("Element with id 'canvas' not found on page")
|
||||
println("element loaded")
|
||||
element.append { block() }
|
||||
}
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun main(): Unit = withCanvas {
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Histogram demo" }
|
||||
plotDiv(Global) {
|
||||
val rnd = Random(222)
|
||||
histogram {
|
||||
name = "Random data"
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
x.numbers = List(500) { rnd.nextDouble() }
|
||||
delay(300)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout {
|
||||
bargap = 0.1
|
||||
title {
|
||||
text = "Basic Histogram"
|
||||
font {
|
||||
size = 20
|
||||
color("black")
|
||||
}
|
||||
}
|
||||
xaxis {
|
||||
title {
|
||||
text = "Value"
|
||||
font {
|
||||
size = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
yaxis {
|
||||
title {
|
||||
text = "Count"
|
||||
font {
|
||||
size = 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Dynamic trace demo" }
|
||||
plotDiv(Global) {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
type = TraceType.scatter
|
||||
}
|
||||
scatter {
|
||||
x(2, 3, 4, 5)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.lines
|
||||
type = TraceType.scatter
|
||||
|
||||
GlobalScope.launch {
|
||||
while (isActive) {
|
||||
delay(500)
|
||||
marker {
|
||||
if (Random.nextBoolean()) {
|
||||
color("magenta")
|
||||
} else {
|
||||
color("blue")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(12, 5, 2, 12)
|
||||
mode = ScatterMode.`lines+markers`
|
||||
type = TraceType.scatter
|
||||
marker {
|
||||
color("red")
|
||||
}
|
||||
}
|
||||
layout {
|
||||
title = "Line and Scatter Plot"
|
||||
}
|
||||
}
|
||||
}
|
||||
div {
|
||||
style = "height:50%; width=100%;"
|
||||
h1 { +"Deserialization" }
|
||||
val plot = Plotly.plot {
|
||||
scatter {
|
||||
x(1, 2, 3, 4)
|
||||
y(10, 15, 13, 17)
|
||||
mode = ScatterMode.markers
|
||||
type = TraceType.scatter
|
||||
}
|
||||
}
|
||||
val serialized = plot.toJsonString()
|
||||
println(serialized)
|
||||
val deserialized = Plot(Json.decodeFromString(MetaSerializer, serialized))
|
||||
plotDiv(plot = deserialized)
|
||||
// plotDiv(plot = deserialized).on(PlotlyEventListenerType.CLICK){
|
||||
// println(it.toString())
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>Kotlin/Wasm Example</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script src="wasm-demo.js"></script>
|
||||
|
||||
<div id="canvas"></div>
|
||||
|
||||
</body>
|
||||
|
||||
<script type="application/javascript">
|
||||
const unhandledError = (event, error) => {
|
||||
if (error instanceof WebAssembly.CompileError) {
|
||||
document.getElementById("warning").style.display = "initial";
|
||||
|
||||
// Hide a Scary Webpack Overlay which is less informative in this case.
|
||||
const webpackOverlay = document.getElementById("webpack-dev-server-client-overlay");
|
||||
if (webpackOverlay != null) webpackOverlay.style.display = "none";
|
||||
}
|
||||
}
|
||||
addEventListener("error", (event) => unhandledError(event, event.error));
|
||||
addEventListener("unhandledrejection", (event) => unhandledError(event, event.reason));
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -11,11 +11,9 @@ val plotlyVersion by extra("2.35.3")
|
||||
//}
|
||||
|
||||
kscience {
|
||||
// jvm()
|
||||
// js()
|
||||
fullStack(bundleName = "js/plotly-kt.js")
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useSerialization()
|
||||
|
||||
commonMain {
|
||||
@@ -29,6 +27,11 @@ kscience {
|
||||
nativeMain {
|
||||
implementation("com.squareup.okio:okio:3.3.0")
|
||||
}
|
||||
|
||||
wasmJsMain {
|
||||
api(npm("plotly.js", plotlyVersion))
|
||||
api("org.jetbrains.kotlinx:kotlinx-browser:0.5.0")
|
||||
}
|
||||
}
|
||||
|
||||
kotlinJupyter {
|
||||
|
||||
@@ -720,13 +720,11 @@ public fun <T : Scheme> Trace.scheme(
|
||||
|
||||
/**
|
||||
* A base class for Plotly traces
|
||||
*
|
||||
* @param uid a unique identifier for this trace
|
||||
*/
|
||||
@Serializable
|
||||
public open class Trace : AbstractVision(), MutableMetaProvider, MetaRepr {
|
||||
|
||||
override fun get(name: Name): MutableMeta? = properties.get(name)
|
||||
override fun get(name: Name): MutableMeta? = properties[name]
|
||||
|
||||
override fun set(name: Name, node: Meta?) {
|
||||
properties[name] = node
|
||||
|
||||
@@ -61,9 +61,10 @@ public fun Element.plot(
|
||||
trace.eventFlow.filterIsInstance<VisionPropertyChangedEvent>().onEach { event ->
|
||||
val traceData = trace.toDynamic()
|
||||
|
||||
//need to wrap coordinates into an additional array because plotly API for some reason expects 2D arrays
|
||||
Plotly.coordinateNames.forEach { coordinate ->
|
||||
val data = traceData[coordinate]
|
||||
if (traceData[coordinate] != null) {
|
||||
if (data != null) {
|
||||
traceData[coordinate] = arrayOf(data)
|
||||
}
|
||||
}
|
||||
@@ -111,12 +112,24 @@ public class PlotlyElement(public val div: HTMLElement)
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@Deprecated("Change arguments positions", ReplaceWith("plotDiv(plot, plotlyConfig, scope)"))
|
||||
public fun TagConsumer<HTMLElement>.plotDiv(
|
||||
plotlyConfig: PlotlyConfig,
|
||||
plot: Plot,
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
|
||||
/**
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun TagConsumer<HTMLElement>.plotDiv(
|
||||
plot: Plot,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
/**
|
||||
* Render plot in the HTML element using direct plotly API.
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,6 @@ import okio.Path.Companion.toPath
|
||||
/**
|
||||
* Create a standalone html with the plot
|
||||
* @param path the reference to html file. If null, create a temporary file
|
||||
* @param show if true, start the browser after file is created
|
||||
* @param config represents plotly frame configuration
|
||||
*/
|
||||
@UnstablePlotlyAPI
|
||||
|
||||
57
plotly-kt/plotly-kt-core/src/wasmJsMain/kotlin/PlotlyWasm.kt
Normal file
57
plotly-kt/plotly-kt-core/src/wasmJsMain/kotlin/PlotlyWasm.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
@file:OptIn(ExperimentalWasmJsInterop::class)
|
||||
|
||||
package space.kscience.plotly
|
||||
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.events.MouseEvent
|
||||
import kotlin.js.Promise
|
||||
|
||||
public external interface ToImgOpts {
|
||||
public var format: JsString /* 'jpeg' | 'png' | 'webp' | 'svg' */
|
||||
public var width: JsNumber
|
||||
public var height: JsNumber
|
||||
}
|
||||
|
||||
public external interface DownloadImgOpts {
|
||||
public var format: JsString /* 'jpeg' | 'png' | 'webp' | 'svg' */
|
||||
public var width: JsNumber
|
||||
public var height: JsNumber
|
||||
public var filename: JsString
|
||||
}
|
||||
|
||||
|
||||
@JsName("Plotly")
|
||||
@JsModule("plotly.js/dist/plotly.js")
|
||||
public external object PlotlyWasm {
|
||||
public fun newPlot(
|
||||
graphDiv: Element,
|
||||
data: JsArray<JsAny> = definedExternally,
|
||||
layout: JsAny = definedExternally,
|
||||
config: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun react(
|
||||
graphDiv: Element,
|
||||
data: JsArray<JsAny> = definedExternally,
|
||||
layout: JsAny = definedExternally,
|
||||
config: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun update(
|
||||
graphDiv: Element,
|
||||
data: JsAny = definedExternally,
|
||||
layout: JsAny = definedExternally
|
||||
)
|
||||
|
||||
public fun restyle(graphDiv: Element, update: JsAny, traceIndices: JsArray<JsNumber>? = definedExternally)
|
||||
public fun relayout(graphDiv: Element, update: JsAny)
|
||||
|
||||
public fun toImage(root: Element, opts: ToImgOpts): Promise<JsString>
|
||||
public fun downloadImage(root: Element, opts: DownloadImgOpts): Promise<JsString>
|
||||
}
|
||||
|
||||
|
||||
public external interface PlotMouseEvent {
|
||||
public val points: JsArray<JsAny>
|
||||
public val event: MouseEvent
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
@file:OptIn(ExperimentalWasmJsInterop::class)
|
||||
|
||||
package space.kscience.plotly
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.html.TagConsumer
|
||||
import kotlinx.html.div
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.MutationObserver
|
||||
import org.w3c.dom.MutationObserverInit
|
||||
import org.w3c.dom.MutationRecord
|
||||
import space.kscience.dataforge.meta.MetaRepr
|
||||
import space.kscience.dataforge.meta.MetaSerializer
|
||||
import space.kscience.dataforge.meta.toJson
|
||||
import space.kscience.plotly.Plotly.coordinateNames
|
||||
import space.kscience.plotly.models.Trace
|
||||
import space.kscience.visionforge.VisionGroupCompositionChangedEvent
|
||||
import space.kscience.visionforge.VisionPropertyChangedEvent
|
||||
|
||||
@JsFun("s => JSON.parse(s)")
|
||||
private external fun json(s: String): JsAny
|
||||
|
||||
private fun JsonElement.toWasmJs(): JsAny {
|
||||
val string = toString()
|
||||
return json(string)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private fun MetaRepr.toWasmJs(): JsAny = Json.encodeToJsonElement(MetaSerializer, toMeta()).toWasmJs()
|
||||
|
||||
private fun List<MetaRepr>.toWasmJs(): JsArray<JsAny> = map { it.toWasmJs() }.toJsArray()
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun myMutationObserverInit(
|
||||
childList: Boolean?,
|
||||
attributes: Boolean?,
|
||||
): MutationObserverInit = js("({ childList: childList, attributes: attributes})")
|
||||
|
||||
|
||||
/**
|
||||
* Attach a plot to this element or update the existing plot
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun Element.plot(
|
||||
plotlyConfig: PlotlyConfig,
|
||||
plot: Plot,
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope
|
||||
) {
|
||||
//send initial data
|
||||
PlotlyWasm.react(
|
||||
graphDiv = this,
|
||||
data = plot.data.toWasmJs(),
|
||||
layout = plot.layout.toWasmJs(),
|
||||
config = plotlyConfig.toWasmJs()
|
||||
)
|
||||
|
||||
//start updates
|
||||
val listenJob = scope.launch {
|
||||
plot.data.forEachIndexed { index, trace: Trace ->
|
||||
trace.eventFlow.filterIsInstance<VisionPropertyChangedEvent>().onEach { event ->
|
||||
|
||||
val traceMeta = trace.toMeta()
|
||||
|
||||
//wrap coordinates into an additional array because plotly API for some reason expects 2D arrays
|
||||
val traceJson = JsonObject(
|
||||
traceMeta.items.map { (token, item) ->
|
||||
val key = token.toStringUnescaped()
|
||||
val valueUnwrapped = item.toJson()
|
||||
val value = if (key in coordinateNames) JsonArray(listOf(valueUnwrapped)) else valueUnwrapped
|
||||
key to value
|
||||
}.toMap()
|
||||
)
|
||||
|
||||
PlotlyWasm.restyle(this@plot, traceJson.toWasmJs(), listOf(index.toJsNumber()).toJsArray())
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
plot.eventFlow.onEach { event ->
|
||||
when (event) {
|
||||
is VisionGroupCompositionChangedEvent -> PlotlyWasm.react(this@plot, plot.data.toWasmJs())
|
||||
is VisionPropertyChangedEvent -> PlotlyWasm.relayout(this@plot, plot.layout.toWasmJs())
|
||||
else -> {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
//observe node removal to avoid memory leak
|
||||
MutationObserver { records: JsArray<MutationRecord>, _ ->
|
||||
if (records.toList().firstOrNull()?.removedNodes?.length != 0) {
|
||||
listenJob.cancel()
|
||||
}
|
||||
}.observe(this, myMutationObserverInit(childList = true, attributes = false))
|
||||
}
|
||||
|
||||
@Deprecated("Change arguments positions", ReplaceWith("plot(plotlyConfig, plot)"))
|
||||
public fun Element.plot(plot: Plot, plotlyConfig: PlotlyConfig = PlotlyConfig()): Unit = plot(plotlyConfig, plot)
|
||||
|
||||
/**
|
||||
* Create a plot in this element
|
||||
*/
|
||||
public inline fun Element.plot(
|
||||
scope: CoroutineScope,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
plotBuilder: Plot.() -> Unit
|
||||
) {
|
||||
plot(plotlyConfig, Plot().apply(plotBuilder), scope)
|
||||
}
|
||||
|
||||
public class PlotlyElement(public val div: Element)
|
||||
|
||||
/**
|
||||
* Create a div element and render the plot in it
|
||||
*/
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
public fun TagConsumer<Element>.plotDiv(
|
||||
plot: Plot,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
scope: CoroutineScope = plot.manager?.context ?: GlobalScope,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(plotlyConfig, plot) })
|
||||
|
||||
/**
|
||||
* Render plot in the HTML element using direct plotly API.
|
||||
*/
|
||||
public inline fun TagConsumer<Element>.plotDiv(
|
||||
scope: CoroutineScope,
|
||||
plotlyConfig: PlotlyConfig = PlotlyConfig(),
|
||||
plotBuilder: Plot.() -> Unit,
|
||||
): PlotlyElement = PlotlyElement(div("plotly-kt-plot").apply { plot(scope, plotlyConfig, plotBuilder) })
|
||||
|
||||
// TODO implement events
|
||||
//@OptIn(ExperimentalSerializationApi::class)
|
||||
//public fun PlotlyElement.on(eventType: PlotlyEventListenerType, block: MouseEvent.(PlotlyEvent) -> Unit) {
|
||||
// div.addEventListener(eventType.eventType) { event: Event ->
|
||||
// val eventData = PlotlyEvent(event.points.map {
|
||||
// PlotlyEventPoint(
|
||||
// curveNumber = it.curveNumber as Int,
|
||||
// pointNumber = it.pointNumber as? Int,
|
||||
// x = Value.of(it.x),
|
||||
// y = Value.of(it.y),
|
||||
// data = Json.decodeFromDynamic(it.data)
|
||||
// )
|
||||
// })
|
||||
// event.event.block(eventData)
|
||||
// }
|
||||
//}
|
||||
@@ -68,5 +68,6 @@ include(
|
||||
// ":plotly:examples:fx-demo",
|
||||
":plotly-kt:examples:compose-demo",
|
||||
":plotly-kt:examples:js-demo",
|
||||
":plotly-kt:examples:native-demo"
|
||||
":plotly-kt:examples:native-demo",
|
||||
":plotly-kt:examples:wasm-demo"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useCoroutines()
|
||||
commonMain {
|
||||
api("space.kscience:dataforge-context:$dataforgeVersion")
|
||||
|
||||
@@ -7,6 +7,8 @@ kscience {
|
||||
js {
|
||||
binaries.library()
|
||||
}
|
||||
// native()
|
||||
// wasmJs()
|
||||
dependencies {
|
||||
api(projects.visionforgeSolid)
|
||||
api("space.kscience:gdml:0.5.0")
|
||||
@@ -14,4 +16,9 @@ kscience {
|
||||
dependencies(jvmTest) {
|
||||
implementation(spclibs.logback.classic)
|
||||
}
|
||||
}
|
||||
|
||||
readme {
|
||||
// TODO remove into a separate library
|
||||
maturity = space.kscience.gradle.Maturity.DEPRECATED
|
||||
}
|
||||
@@ -8,7 +8,7 @@ kscience {
|
||||
jvm()
|
||||
js()
|
||||
native()
|
||||
// wasm()
|
||||
wasmJs()
|
||||
useSerialization {
|
||||
json()
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ kscience {
|
||||
}
|
||||
}
|
||||
}
|
||||
native()
|
||||
wasmJs()
|
||||
|
||||
useSerialization()
|
||||
commonMain {
|
||||
|
||||
Reference in New Issue
Block a user