Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added onTap feature for chartJS #1690

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 72 additions & 11 deletions modules/ensemble/lib/widget/visualization/chart_js.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import 'dart:io';
import 'dart:convert';
import 'dart:math';

import 'package:ensemble/framework/action.dart';
import 'package:ensemble/framework/event.dart';
import 'package:ensemble/screen_controller.dart';
import 'package:ensemble_ts_interpreter/parser/newjs_interpreter.dart';
import 'package:flutter/material.dart';
import 'package:js_widget/js_widget.dart';
import 'package:ensemble/framework/widget/widget.dart';
import 'package:ensemble/util/utils.dart';
import 'package:ensemble/widget/helpers/controllers.dart';
import 'package:ensemble_ts_interpreter/invokables/invokable.dart';
import 'package:ensemble_ts_interpreter/parser/newjs_interpreter.dart';
import 'package:flutter/material.dart';
import 'package:js_widget/js_widget.dart';
import 'dart:convert';

class ChartJsController extends WidgetController {
ChartJsController() {
Expand All @@ -21,6 +22,7 @@ class ChartJsController extends WidgetController {
String get chartId => id!;
dynamic config = '';
Function? evalScript;
EnsembleAction? onTap;
}

class ChartJs extends StatefulWidget
Expand Down Expand Up @@ -125,7 +127,9 @@ class ChartJs extends StatefulWidget
} else {
_controller.config = value;
}
}
},
'onTap': (funcDefinition) => _controller.onTap =
EnsembleAction.from(funcDefinition, initiator: this),
};
}
}
Expand Down Expand Up @@ -161,15 +165,72 @@ class ChartJsState extends EWidgetState<ChartJs> {
id: widget.controller.id!,
createHtmlTag: () =>
'<div id="${widget.controller.chartDiv}"><canvas id="${widget.controller.chartId}"></canvas></div>',
scriptToInstantiate: (String c) {
return 'if (typeof ${widget.controller.chartVar} !== "undefined") ${widget.controller.chartVar}.destroy();${widget.controller.chartVar} = new Chart(document.getElementById("${widget.controller.chartId}"), $c);${widget.controller.chartVar}.update();';
scriptToInstantiate: (String config) {
return '''
if (typeof ${widget.controller.chartVar} !== "undefined") {
${widget.controller.chartVar}.destroy();
}
${widget.controller.chartVar} = new Chart(document.getElementById("${widget.controller.chartId}"), $config);

// Add click event listener to the chart
document.getElementById("${widget.controller.chartId}").onclick = function(event) {
var activePoints = ${widget.controller.chartVar}.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true);
if (activePoints.length > 0) {
var firstPoint = activePoints[0];
var datasetIndex = firstPoint.datasetIndex;
var index = firstPoint.index;
var dataset = ${widget.controller.chartVar}.data.datasets[datasetIndex] || {};
var label = ${widget.controller.chartVar}.data.labels[index] || '';
var value = dataset.data ? dataset.data[index] : '';
var datasetLabel = dataset.label || '';
var backgroundColor = dataset.backgroundColor || '';
var borderColor = dataset.borderColor || '';
var x = firstPoint.element.x || 0;
var y = firstPoint.element.y || 0;
var chartType = ${widget.controller.chartVar}.config.type || '';
// Serialize options safely
var options = JSON.parse(JSON.stringify(${widget.controller.chartVar}.options, function(key, value) {
if (typeof value === 'function') {
return value.toString();
}
return value;
})) || {};
var data = {
label: label,
value: value,
datasetLabel: datasetLabel,
datasetIndex: datasetIndex,
index: index,
backgroundColor: backgroundColor,
borderColor: borderColor,
x: x,
y: y,
chartType: chartType,
options: options
};
if (window.sendMessageToFlutter) {
window.sendMessageToFlutter(JSON.stringify(data));
} else {
console.log("Flutter handler not available");
}
}
};

${widget.controller.chartVar}.update();
''';
},
size: Size(widget.controller.width.toDouble(),
widget.controller.height.toDouble()),
size: Size(widget.controller.width.toDouble(), widget.controller.height.toDouble()),
data: widget.controller.config,
scripts: const [
"https://cdn.jsdelivr.net/npm/chart.js",
],
listener: (msg) {
if (widget.controller.onTap != null) {
Map<String, dynamic> data = jsonDecode(msg);
ScreenController().executeAction(context, widget.controller.onTap!,
event: EnsembleEvent(widget, data: data));
}
},
);
return jsWidget!;
}
Expand Down
21 changes: 20 additions & 1 deletion modules/js_widget/lib/src/mobile/js_widget.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
// Import for Android features.
Expand Down Expand Up @@ -75,6 +77,14 @@ class JsWidgetState extends State<JsWidget> {
controller = WebViewController.fromPlatformCreationParams(params)
..setBackgroundColor(Colors.transparent)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'JsBridge',
onMessageReceived: (JavaScriptMessage message) {
if (widget.listener != null) {
widget.listener!(message.message);
}
},
)
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
Expand Down Expand Up @@ -151,6 +161,14 @@ class JsWidgetState extends State<JsWidget> {
for (String src in widget.scripts) {
html += '<script async="false" src="$src"></script>';
}
html += '''
<script>
// Expose sendMessageToFlutter function to JavaScript
window.sendMessageToFlutter = function(message) {
JsBridge.postMessage(message);
};
</script>
''';
html += '</body></html>';
return html;
}
Expand All @@ -160,7 +178,8 @@ class JsWidgetState extends State<JsWidget> {
_isLoaded = true;
});
controller.runJavaScript('''
${widget.preCreateScript != null ? widget.preCreateScript!() : ''}
${widget.scriptToInstantiate(widget.data)}
''');
''');
}
}
35 changes: 22 additions & 13 deletions modules/js_widget/lib/src/web/js_widget.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:html' as html;
import 'dart:js' as js; // Import dart:js for interop
import 'dart:math';
import 'dart:ui' as ui;

Expand Down Expand Up @@ -27,29 +28,29 @@ class JsWidget extends StatefulWidget {

///Custom `loader` widget, until script is loaded
///
///Has no effect on Web
/// Has no effect on Web
///
///Defaults to `CircularProgressIndicator`
/// Defaults to `CircularProgressIndicator`
final Widget loader;

///Widget data
/// Widget data
final String id;
final Function scriptToInstantiate;
final Function createHtmlTag;
final Function? preCreateScript;
final String data;
Function(String msg)? listener;

///Widget size
/// Widget size
///
///Height and width of the widget is required
/// Height and width of the widget is required
///
///```dart
///Size size = Size(400, 300);
///```
/// ```dart
/// Size size = Size(400, 300);
/// ```
final Size size;

///Scripts to be loaded
/// Scripts to be loaded
final List<String> scripts;
@override
JsWidgetState createState() => JsWidgetState();
Expand Down Expand Up @@ -85,6 +86,13 @@ class JsWidgetState extends State<JsWidget> {
}
}

void init(Function(String id, String msg) globalListener) {
// Expose the 'sendMessageToFlutter' function to JavaScript
js.context['sendMessageToFlutter'] = (dynamic msg) {
globalListener(widget.id, msg as String);
};
}

@override
void didUpdateWidget(covariant JsWidget oldWidget) {
if (oldWidget.data != widget.data ||
Expand All @@ -98,9 +106,9 @@ class JsWidgetState extends State<JsWidget> {

@override
void initState() {
init(globalListener);
if (widget.listener != null) {
addListener(widget.id, widget.listener!);
init(globalListener);
}
if (widget.preCreateScript != null) {
eval(widget.preCreateScript!());
Expand All @@ -124,9 +132,10 @@ class JsWidgetState extends State<JsWidget> {
@override
Widget build(BuildContext context) {
return SizedBox(
height: widget.size.height,
width: widget.size.width,
child: HtmlElementView(viewType: widget.id));
height: widget.size.height,
width: widget.size.width,
child: HtmlElementView(viewType: widget.id),
);
}

Future<bool> _load() {
Expand Down
Loading