Working JavaScript charts
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 20 Sep 2023 07:36:20 +0000 (09:36 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 20 Sep 2023 07:36:20 +0000 (09:36 +0200)
20 files changed:
js/package-lock.json
js/package.json
js/src/chart/BarChart.js [new file with mode: 0644]
js/src/chart/ChartJsPart.js [new file with mode: 0644]
js/src/chart/ChartPart.js [new file with mode: 0644]
js/src/chart/TestGraph.js [new file with mode: 0644]
js/src/chart/export-package.js [new file with mode: 0644]
js/src/chart/index.html [new file with mode: 0644]
js/src/chart/index.js [new file with mode: 0644]
js/src/graph/TestGraph.js [deleted file]
js/src/graph/export-package.js [deleted file]
js/src/graph/index.html [deleted file]
js/src/graph/index.js [deleted file]
js/webpack.common.js
swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/MapUiProvider.java
swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJSMapPart.java [deleted file]
swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJsMapPart.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/AbstractJsChart.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/SwtJsBarChart.java [new file with mode: 0644]
swt/org.argeo.app.swt/src/org/argeo/app/swt/js/SwtBrowserJsPart.java

index ec19936b63a5b2213b8368cafa767865334ef417..7e38ea86713cf75a685a87c8364697d6ac870799 100644 (file)
@@ -11,6 +11,7 @@
                        "dependencies": {
                                "@nieuwlandgeo/sldreader": "0.3.x",
                                "chart.js": "4.x.x",
+                               "chartjs-plugin-annotation": "^3.0.1",
                                "ol": "8.x.x"
                        },
                        "devDependencies": {
                                "pnpm": ">=7"
                        }
                },
+               "node_modules/chartjs-plugin-annotation": {
+                       "version": "3.0.1",
+                       "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.0.1.tgz",
+                       "integrity": "sha512-hlIrXXKqSDgb+ZjVYHefmlZUXK8KbkCPiynSVrTb/HjTMkT62cOInaT1NTQCKtxKKOm9oHp958DY3RTAFKtkHg==",
+                       "peerDependencies": {
+                               "chart.js": ">=4.0.0"
+                       }
+               },
                "node_modules/chownr": {
                        "version": "2.0.0",
                        "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
                                "@kurkle/color": "^0.3.0"
                        }
                },
+               "chartjs-plugin-annotation": {
+                       "version": "3.0.1",
+                       "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.0.1.tgz",
+                       "integrity": "sha512-hlIrXXKqSDgb+ZjVYHefmlZUXK8KbkCPiynSVrTb/HjTMkT62cOInaT1NTQCKtxKKOm9oHp958DY3RTAFKtkHg==",
+                       "requires": {}
+               },
                "chownr": {
                        "version": "2.0.0",
                        "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
index 08a3865c47b214580d49f20de3edf253df50e7cc..4cef5e91b090a4456100187a2804c099b8713e12 100644 (file)
@@ -25,6 +25,7 @@
        "dependencies": {
                "@nieuwlandgeo/sldreader": "0.3.x",
                "chart.js": "4.x.x",
+               "chartjs-plugin-annotation": "^3.0.1",
                "ol": "8.x.x"
        }
 }
diff --git a/js/src/chart/BarChart.js b/js/src/chart/BarChart.js
new file mode 100644 (file)
index 0000000..d65b9cc
--- /dev/null
@@ -0,0 +1,27 @@
+import Chart from 'chart.js/auto';
+
+import ChartJsPart from './ChartJsPart.js';
+
+export default class BarChart extends ChartJsPart {
+       /** Constructor taking the mapName as an argument. */
+       constructor(chartName) {
+               super(chartName);
+               this.setChart(new Chart(this.getChartCanvas(), {
+                       type: 'bar',
+                       data: {
+                               datasets: []
+                       },
+                       options: {
+                               scales: {
+                                       y: {
+                                               beginAtZero: true
+                                       },
+                               },
+                               animation: false,
+                       }
+               }));
+
+       }
+
+
+}
diff --git a/js/src/chart/ChartJsPart.js b/js/src/chart/ChartJsPart.js
new file mode 100644 (file)
index 0000000..ac60ce3
--- /dev/null
@@ -0,0 +1,65 @@
+import ChartPart from './ChartPart.js';
+
+import { Chart } from 'chart.js';
+import annotationPlugin from 'chartjs-plugin-annotation';
+
+Chart.register(annotationPlugin);
+
+export default class ChartJsPart extends ChartPart {
+       #chart;
+
+       /** Constructor taking the mapName as an argument. */
+       constructor(chartName) {
+               super(chartName);
+       }
+
+       setChart(chart) {
+               this.#chart = chart;
+       }
+
+       getChart() {
+               return this.#chart;
+       }
+
+       //
+       // DATA
+       //
+       setLabels(labels) {
+               const chart = this.getChart();
+               chart.data.labels = labels;
+               this.update();
+       }
+
+       addDataset(label, data) {
+               const chart = this.getChart();
+               chart.data.datasets.push({
+                       label: label,
+                       data: data,
+                       borderWidth: 1
+               });
+               this.update();
+       }
+
+       setData(labels, label, data) {
+               this.clearDatasets();
+               this.setLabels(labels);
+               this.addDataset(label, data);
+       }
+
+       setDatasets(labels, datasets) {
+               const chart = this.getChart();
+               chart.data.datasets = datasets;
+               chart.data.labels = labels;
+               this.update();
+       }
+
+       clearDatasets() {
+               const chart = this.getChart();
+               chart.data.datasets = [];
+               this.update();
+       }
+
+       update() {
+               this.#chart.update();
+       }
+}
diff --git a/js/src/chart/ChartPart.js b/js/src/chart/ChartPart.js
new file mode 100644 (file)
index 0000000..1fe9221
--- /dev/null
@@ -0,0 +1,34 @@
+/** API to be used by Java.
+ *  @module MapPart
+ */
+
+/** Abstract base class for displaying a map. */
+export default class ChartPart {
+
+       /** The name of the chart, will also be the name of the variable */
+       #chartName;
+
+       constructor(chartName) {
+               this.#chartName = chartName;
+               this.createChartCanvas(this.#chartName);
+       }
+
+
+       //
+       // HTML
+       //
+       /** Create the div element where the chart will be displayed. */
+       createChartCanvas(id) {
+               const chartDiv = document.createElement('canvas');
+               chartDiv.id = id;
+               //chartDiv.style.cssText = 'width: 100%;';
+               chartDiv.style.cssText = 'width: 100%; height: 100vh;';
+               document.body.appendChild(chartDiv);
+       }
+
+       /** Get the div element where the chart is displayed. */
+       getChartCanvas() {
+               return document.getElementById(this.#chartName);
+       }
+
+}
\ No newline at end of file
diff --git a/js/src/chart/TestGraph.js b/js/src/chart/TestGraph.js
new file mode 100644 (file)
index 0000000..9cc67db
--- /dev/null
@@ -0,0 +1,27 @@
+import Chart from 'chart.js/auto';
+
+export default class TestGraph {
+
+       init() {
+               const ctx = document.getElementById('myChart');
+
+               new Chart(ctx, {
+                       type: 'bar',
+                       data: {
+                               labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
+                               datasets: [{
+                                       label: '# of Votes',
+                                       data: [12, 19, 3, 5, 2, 3],
+                                       borderWidth: 1
+                               }]
+                       },
+                       options: {
+                               scales: {
+                                       y: {
+                                               beginAtZero: true
+                                       }
+                               }
+                       }
+               });
+       }
+}
diff --git a/js/src/chart/export-package.js b/js/src/chart/export-package.js
new file mode 100644 (file)
index 0000000..5fbcc0a
--- /dev/null
@@ -0,0 +1,21 @@
+import BarChart from './BarChart.js';
+import TestGraph from './TestGraph.js';
+//import { rectY, binX } from "@observablehq/plot";
+
+// PSEUDO PACKAGE
+if (typeof globalThis.argeo === 'undefined')
+       globalThis.argeo = {};
+if (typeof globalThis.argeo.app === 'undefined')
+       globalThis.argeo.app = {};
+if (typeof globalThis.argeo.app.chart === 'undefined')
+       globalThis.argeo.app.chart = {};
+
+// PUBLIC CLASSES
+globalThis.argeo.app.chart.BarChart = BarChart;
+globalThis.argeo.app.chart.TestGraph = TestGraph;
+
+//const plot = rectY({ length: 10000 }, binX({ y: "count" }, { x: Math.random })).plot();
+//const div = document.querySelector("#myplot");
+//div.append(plot);
+
+"use strict";
diff --git a/js/src/chart/index.html b/js/src/chart/index.html
new file mode 100644 (file)
index 0000000..72f6094
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+       <meta charset="UTF-8">
+</head>
+
+<body>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/js/src/chart/index.js b/js/src/chart/index.js
new file mode 100644 (file)
index 0000000..6ff32b0
--- /dev/null
@@ -0,0 +1 @@
+import './export-package.js';
diff --git a/js/src/graph/TestGraph.js b/js/src/graph/TestGraph.js
deleted file mode 100644 (file)
index 9cc67db..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-import Chart from 'chart.js/auto';
-
-export default class TestGraph {
-
-       init() {
-               const ctx = document.getElementById('myChart');
-
-               new Chart(ctx, {
-                       type: 'bar',
-                       data: {
-                               labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
-                               datasets: [{
-                                       label: '# of Votes',
-                                       data: [12, 19, 3, 5, 2, 3],
-                                       borderWidth: 1
-                               }]
-                       },
-                       options: {
-                               scales: {
-                                       y: {
-                                               beginAtZero: true
-                                       }
-                               }
-                       }
-               });
-       }
-}
diff --git a/js/src/graph/export-package.js b/js/src/graph/export-package.js
deleted file mode 100644 (file)
index ec8b4ee..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-import TestGraph from './TestGraph.js';
-//import { rectY, binX } from "@observablehq/plot";
-
-// PSEUDO PACKAGE
-if (typeof globalThis.argeo === 'undefined')
-       globalThis.argeo = {};
-if (typeof globalThis.argeo.app === 'undefined')
-       globalThis.argeo.app = {};
-if (typeof globalThis.argeo.app.graph === 'undefined')
-       globalThis.argeo.app.graph = {};
-
-// PUBLIC CLASSES
-globalThis.argeo.app.graph.TestGraph = TestGraph;
-
-//const plot = rectY({ length: 10000 }, binX({ y: "count" }, { x: Math.random })).plot();
-//const div = document.querySelector("#myplot");
-//div.append(plot);
-
-"use strict";
diff --git a/js/src/graph/index.html b/js/src/graph/index.html
deleted file mode 100644 (file)
index b9115a8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-       <meta charset="UTF-8">
-</head>
-
-<body>
-       <canvas id="myChart"></canvas>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/js/src/graph/index.js b/js/src/graph/index.js
deleted file mode 100644 (file)
index 6ff32b0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-import './export-package.js';
index dccac82c4d767ac6163f2cebb1d6ddc5041e37b2..ba57965fd4aeceb226b08a643a85b3e907994e3c 100644 (file)
@@ -6,7 +6,7 @@ const path = require('path');
 module.exports = {
        entry: {
                "geo": './src/geo/index.js',
-               "graph": './src/graph/index.js',
+               "chart": './src/chart/index.js',
        },
        output: {
                filename: '[name].[contenthash].js',
@@ -50,11 +50,11 @@ module.exports = {
                        chunks: ['geo'],
                }),
                new HtmlWebpackPlugin({
-                       title: 'Argeo Suite Graph JS',
-                       template: 'src/graph/index.html',
+                       title: 'Argeo Suite Chart JS',
+                       template: 'src/chart/index.html',
                        scriptLoading: 'module',
-                       filename: 'graph.html',
-                       chunks: ['graph'],
+                       filename: 'chart.html',
+                       chunks: ['chart'],
                }),
 
        ],
index fa70146f3d48e557ae5720e1abb8a596e66b2495..b75892371c68f5b65416d704f230364a8b239681 100644 (file)
@@ -10,7 +10,7 @@ public class MapUiProvider implements SwtUiProvider {
 
        @Override
        public Control createUiPart(Composite parent, Content context) {
-               SwtJSMapPart map = new SwtJSMapPart("defaultOverviewMap", parent, 0);
+               SwtJsMapPart map = new SwtJsMapPart("defaultOverviewMap", parent, 0);
                map.setCenter(13.404954, 52.520008); // Berlin
 //             map.setCenter(-74.00597, 40.71427); // NYC
 //             map.addPoint(-74.00597, 40.71427, null);
diff --git a/swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJSMapPart.java b/swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJSMapPart.java
deleted file mode 100644 (file)
index ad1a9fa..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.argeo.app.geo.swt;
-
-import java.util.concurrent.CompletionStage;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import org.argeo.app.geo.GeoUtils;
-import org.argeo.app.geo.ux.JsImplementation;
-import org.argeo.app.geo.ux.MapPart;
-import org.argeo.app.swt.js.SwtBrowserJsPart;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * An SWT implementation of {@link MapPart} based on JavaScript.
- */
-public class SwtJSMapPart extends SwtBrowserJsPart implements MapPart {
-       static final long serialVersionUID = 2713128477504858552L;
-
-       private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
-       private final String mapName;// = "argeoMap";
-
-       public SwtJSMapPart(String mapName, Composite parent, int style) {
-               super(parent, style, "/pkg/org.argeo.app.js/geo.html");
-               this.mapName = mapName;
-       }
-
-       @Override
-       protected void init() {
-               // create map
-               doExecute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
-       }
-
-       /*
-        * MapPart.js METHODS
-        */
-
-       @Override
-       public void addPoint(double lng, double lat, String style) {
-               callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
-       }
-
-       @Override
-       public void addUrlLayer(String url, GeoFormat format, String style) {
-               callMapMethod("addUrlLayer('%s', '%s', %s, false)", url, format.name(), style);
-       }
-
-       public void addCssUrlLayer(String url, GeoFormat format, String css) {
-               String style = GeoUtils.createSldFromCss("layer", "Layer", css);
-               callMapMethod("addUrlLayer('%s', '%s', '%s', true)", url, format.name(), style);
-       }
-
-       @Override
-       public void setZoom(int zoom) {
-               callMapMethod("setZoom(%d)", zoom);
-       }
-
-       @Override
-       public void setCenter(double lng, double lat) {
-               callMapMethod("setCenter(%f, %f)", lng, lat);
-       }
-
-       protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
-               return callMethod(getJsMapVar(), methodCall, args);
-       }
-
-       private String getJsMapVar() {
-               return getJsVarName(mapName);
-       }
-
-       /*
-        * CALLBACKS
-        */
-       public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
-               addCallback("FeatureSelected", (arr) -> {
-                       toDo.accept(new FeatureSelectedEvent((String) arr[0]));
-                       return null;
-               });
-       }
-
-       public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
-               addCallback("FeatureSingleClick", (arr) -> {
-                       toDo.accept(new FeatureSingleClickEvent((String) arr[0]));
-                       return null;
-               });
-       }
-
-       public void onFeaturePopup(Function<FeaturePopupEvent, String> toDo) {
-               addCallback("FeaturePopup", (arr) -> {
-                       return toDo.apply(new FeaturePopupEvent((String) arr[0]));
-               });
-       }
-
-       protected void addCallback(String suffix, Function<Object[], Object> toDo) {
-               getReadyStage().thenAccept((ready) -> {
-                       String functionName = createJsFunction(mapName + "__on" + suffix, toDo);
-                       doExecute(getJsMapVar() + ".callbacks['on" + suffix + "']=" + functionName + ";");
-                       callMethod(mapName, "enable" + suffix + "()");
-               });
-       }
-}
diff --git a/swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJsMapPart.java b/swt/org.argeo.app.geo.swt/src/org/argeo/app/geo/swt/SwtJsMapPart.java
new file mode 100644 (file)
index 0000000..5e90d39
--- /dev/null
@@ -0,0 +1,99 @@
+package org.argeo.app.geo.swt;
+
+import java.util.concurrent.CompletionStage;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.argeo.app.geo.GeoUtils;
+import org.argeo.app.geo.ux.JsImplementation;
+import org.argeo.app.geo.ux.MapPart;
+import org.argeo.app.swt.js.SwtBrowserJsPart;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * An SWT implementation of {@link MapPart} based on JavaScript.
+ */
+public class SwtJsMapPart extends SwtBrowserJsPart implements MapPart {
+       static final long serialVersionUID = 2713128477504858552L;
+
+       private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
+       private final String mapName;// = "argeoMap";
+
+       public SwtJsMapPart(String mapName, Composite parent, int style) {
+               super(parent, style, "/pkg/org.argeo.app.js/geo.html");
+               this.mapName = mapName;
+       }
+
+       @Override
+       protected void init() {
+               // create map
+               doExecute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
+       }
+
+       /*
+        * MapPart.js METHODS
+        */
+
+       @Override
+       public void addPoint(double lng, double lat, String style) {
+               callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
+       }
+
+       @Override
+       public void addUrlLayer(String url, GeoFormat format, String style) {
+               callMapMethod("addUrlLayer('%s', '%s', %s, false)", url, format.name(), style);
+       }
+
+       public void addCssUrlLayer(String url, GeoFormat format, String css) {
+               String style = GeoUtils.createSldFromCss("layer", "Layer", css);
+               callMapMethod("addUrlLayer('%s', '%s', '%s', true)", url, format.name(), style);
+       }
+
+       @Override
+       public void setZoom(int zoom) {
+               callMapMethod("setZoom(%d)", zoom);
+       }
+
+       @Override
+       public void setCenter(double lng, double lat) {
+               callMapMethod("setCenter(%f, %f)", lng, lat);
+       }
+
+       protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
+               return callMethod(getJsMapVar(), methodCall, args);
+       }
+
+       private String getJsMapVar() {
+               return getJsVarName(mapName);
+       }
+
+       /*
+        * CALLBACKS
+        */
+       public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
+               addCallback("FeatureSelected", (arr) -> {
+                       toDo.accept(new FeatureSelectedEvent((String) arr[0]));
+                       return null;
+               });
+       }
+
+       public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
+               addCallback("FeatureSingleClick", (arr) -> {
+                       toDo.accept(new FeatureSingleClickEvent((String) arr[0]));
+                       return null;
+               });
+       }
+
+       public void onFeaturePopup(Function<FeaturePopupEvent, String> toDo) {
+               addCallback("FeaturePopup", (arr) -> {
+                       return toDo.apply(new FeaturePopupEvent((String) arr[0]));
+               });
+       }
+
+       protected void addCallback(String suffix, Function<Object[], Object> toDo) {
+               getReadyStage().thenAccept((ready) -> {
+                       String functionName = createJsFunction(mapName + "__on" + suffix, toDo);
+                       doExecute(getJsMapVar() + ".callbacks['on" + suffix + "']=" + functionName + ";");
+                       callMethod(mapName, "enable" + suffix + "()");
+               });
+       }
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/AbstractJsChart.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/AbstractJsChart.java
new file mode 100644 (file)
index 0000000..f114621
--- /dev/null
@@ -0,0 +1,27 @@
+package org.argeo.app.swt.chart;
+
+import org.argeo.app.swt.js.SwtBrowserJsPart;
+import org.eclipse.swt.widgets.Composite;
+
+/** Base class for charts. */
+public abstract class AbstractJsChart extends SwtBrowserJsPart {
+       private String chartName;
+
+       protected abstract String getJsImplementation();
+
+       public AbstractJsChart(String chartName, Composite parent, int style) {
+               super(parent, style, "/pkg/org.argeo.app.js/chart.html");
+               this.chartName = chartName;
+       }
+
+       @Override
+       protected void init() {
+               // create chart
+               doExecute(getJsChartVar() + " = new " + getJsImplementation() + "('" + chartName + "');");
+       }
+
+       protected String getJsChartVar() {
+               return getJsVarName(chartName);
+       }
+
+}
diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/SwtJsBarChart.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/chart/SwtJsBarChart.java
new file mode 100644 (file)
index 0000000..332d0eb
--- /dev/null
@@ -0,0 +1,61 @@
+package org.argeo.app.swt.chart;
+
+import java.io.StringWriter;
+
+import org.eclipse.swt.widgets.Composite;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonGenerator;
+
+public class SwtJsBarChart extends AbstractJsChart {
+
+       public SwtJsBarChart(String chartName, Composite parent, int style) {
+               super(chartName, parent, style);
+       }
+
+       @Override
+       protected String getJsImplementation() {
+               return "globalThis.argeo.app.chart.BarChart";
+       }
+
+       public void setLabels(String[] labels) {
+               callMethod(getJsChartVar(), "setLabels(%s)", toJsArray(labels));
+       }
+
+       public void addDataset(String label, int[] values) {
+               callMethod(getJsChartVar(), "addDataset('%s', %s)", label, toJsArray(values));
+       }
+
+       public void setData(String[] labels, String label, int[] values) {
+               callMethod(getJsChartVar(), "setData(%s, '%s', %s)", toJsArray(labels), label, toJsArray(values));
+       }
+
+       public void setDatasets(String[] labels, String[] label, int[][] values) {
+               callMethod(getJsChartVar(), "setDatasets(%s, %s)", toJsArray(labels), toDatasets(label, values));
+       }
+
+       protected String toDatasets(String[] label, int[][] values) {
+               if (label.length != values.length)
+                       throw new IllegalArgumentException("Arrays must have the same length");
+               StringWriter writer = new StringWriter();
+               JsonGenerator g = Json.createGenerator(writer);
+               g.writeStartArray();
+               for (int i = 0; i < label.length; i++) {
+                       g.writeStartObject();
+                       g.write("label", label[i]);
+                       g.writeStartArray("data");
+                       for (int j = 0; j < values[i].length; j++) {
+                               g.write(values[i][j]);
+                       }
+                       g.writeEnd();// data array
+                       g.writeEnd();// dataset
+               }
+               g.writeEnd();
+               g.close();
+               return writer.toString();
+       }
+
+       public void clearDatasets() {
+               callMethod(getJsChartVar(), "clearDatasets()");
+       }
+}
index 8ae34d5ecc10a228b98f35b16b0625aca2d3da12..fac099ffe36bfa65bb746509bdd5874d62071815 100644 (file)
@@ -1,6 +1,8 @@
 package org.argeo.app.swt.js;
 
+import java.util.Arrays;
 import java.util.Locale;
+import java.util.StringJoiner;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 import java.util.function.Function;
@@ -135,10 +137,45 @@ public class SwtBrowserJsPart {
                return GLOBAL_THIS_ + name;
        }
 
+       protected static String toJsArray(int... arr) {
+               return Arrays.toString(arr);
+       }
+
+       protected static String toJsArray(long... arr) {
+               return Arrays.toString(arr);
+       }
+
+       protected static String toJsArray(double... arr) {
+               return Arrays.toString(arr);
+       }
+
+       protected static String toJsArray(String... arr) {
+               return toJsArray((Object[]) arr);
+       }
+
+       protected static String toJsArray(Object... arr) {
+               StringJoiner sj = new StringJoiner(",", "[", "]");
+               for (Object o : arr) {
+                       sj.add(toJsValue(o));
+               }
+               return sj.toString();
+       }
+
+       protected static String toJsValue(Object o) {
+               if (o instanceof CharSequence)
+                       return '\"' + o.toString() + '\"';
+               else if (o instanceof Number)
+                       return o.toString();
+               else if (o instanceof Boolean)
+                       return o.toString();
+               else
+                       return '\"' + o.toString() + '\"';
+       }
+
        /*
         * ACCESSORS
         */
-       
+
        public Control getControl() {
                return browser;
        }