feat: Add initial support for ChartJs plugins (#14433)
+ adding plugin for crosshair and zoom + adding plugin for data labels
This commit is contained in:
parent
aa03edf17c
commit
f1308f3b28
|
@ -6,9 +6,12 @@ import StatusQ.Core 0.1
|
|||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
|
@ -27,15 +30,6 @@ SplitView {
|
|||
{text: "1M", enabled: true}, {text: "6M", enabled: true},
|
||||
{text: "1Y", enabled: true}, {text: "ALL", enabled: true}]
|
||||
|
||||
property var simTimer: Timer {
|
||||
running: true
|
||||
interval: 3000
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
d.generateData();
|
||||
}
|
||||
}
|
||||
|
||||
function minutes(minutes = 0) {
|
||||
var newMinute = new Date(new Date().getTime() - (minutes * 60 * 1000)).toString();
|
||||
if (newMinute.slice(10,12) === "00") {
|
||||
|
@ -80,64 +74,29 @@ SplitView {
|
|||
for (var i = 0; i < timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange].length; ++i) {
|
||||
result[i] = Math.random() * (maxStep - minStep) + minStep;
|
||||
}
|
||||
graphDetail.chart.chartData.datasets[0].data = result;
|
||||
graphDetail.chart.animateToNewData();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
StatusChartPanel {
|
||||
id: graphDetail
|
||||
height: 290
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 24
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
graphsModel: d.graphTabsModel
|
||||
timeRangeModel: d.timeRangeTabsModel
|
||||
onHeaderTabClicked: {
|
||||
//TODO
|
||||
//if time range tab
|
||||
d.generateData();
|
||||
//if graph bar
|
||||
//switch graph
|
||||
}
|
||||
chart.chartType: 'line'
|
||||
chart.chartData: {
|
||||
return {
|
||||
labels: d.timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange],
|
||||
datasets: [{
|
||||
label: 'Price',
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: 3,
|
||||
pointRadius: 0,
|
||||
//data: d.generateData()
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
chart.chartOptions: {
|
||||
return {
|
||||
readonly property var lineConfig: {
|
||||
return {
|
||||
type: 'line',
|
||||
labels: d.timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange],
|
||||
datasets: [{
|
||||
label: 'Price',
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: 3,
|
||||
pointRadius: 0,
|
||||
data: d.generateData()
|
||||
}],
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
//TODO enable zoom
|
||||
// zoom: {
|
||||
// enabled: true,
|
||||
// drag: true,
|
||||
// speed: 0.1,
|
||||
// threshold: 2
|
||||
// },
|
||||
// pan:{enabled:true,mode:'x'},
|
||||
tooltips: {
|
||||
intersect: false,
|
||||
displayColors: false,
|
||||
|
@ -157,6 +116,7 @@ SplitView {
|
|||
xAxes: [{
|
||||
id: 'x-axis-1',
|
||||
position: 'bottom',
|
||||
//type: 'linear',
|
||||
gridLines: {
|
||||
drawOnChartArea: false,
|
||||
drawBorder: false,
|
||||
|
@ -169,34 +129,301 @@ SplitView {
|
|||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
position: 'left',
|
||||
id: 'y-axis-1',
|
||||
gridLines: {
|
||||
borderDash: [8, 4],
|
||||
drawBorder: false,
|
||||
drawTicks: false,
|
||||
color: (Theme.palette.name === "dark") ? '#909090' : '#939BA1'
|
||||
position: 'left',
|
||||
id: 'y-axis-1',
|
||||
gridLines: {
|
||||
borderDash: [8, 4],
|
||||
drawBorder: false,
|
||||
drawTicks: false,
|
||||
color: (Theme.palette.name === "dark") ? '#909090' : '#939BA1'
|
||||
},
|
||||
beforeDataLimits: (axis) => {
|
||||
axis.paddingTop = 25;
|
||||
axis.paddingBottom = 0;
|
||||
},
|
||||
ticks: {
|
||||
fontSize: 10,
|
||||
fontColor: (Theme.palette.name === "dark") ? '#909090' : '#939BA1',
|
||||
padding: 8,
|
||||
min: d.minStep,
|
||||
max: d.maxStep,
|
||||
stepSize: d.stepSize,
|
||||
callback: function(value, index, ticks) {
|
||||
return '$' + value;
|
||||
},
|
||||
beforeDataLimits: (axis) => {
|
||||
axis.paddingTop = 25;
|
||||
axis.paddingBottom = 0;
|
||||
},
|
||||
ticks: {
|
||||
fontSize: 10,
|
||||
fontColor: (Theme.palette.name === "dark") ? '#909090' : '#939BA1',
|
||||
padding: 8,
|
||||
min: d.minStep,
|
||||
max: d.maxStep,
|
||||
stepSize: d.stepSize,
|
||||
callback: function(value, index, ticks) {
|
||||
return '$' + value;
|
||||
},
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var barConfig: {
|
||||
return {
|
||||
type:"bar",
|
||||
options: {
|
||||
onHover: function(event, activeElements) {
|
||||
if (activeElements.length === 0) {
|
||||
toolTip.close()
|
||||
return
|
||||
}
|
||||
|
||||
toolTip.text = "StatusMenu triggered by " + activeElements[0]._model.label
|
||||
toolTip.popup()
|
||||
toolTip.x += 10
|
||||
toolTip.y -= toolTip.height + 10
|
||||
},
|
||||
tooltips: {
|
||||
enabled:false
|
||||
},
|
||||
scales:{
|
||||
xAxes:[{
|
||||
id: "x-axis-1",
|
||||
position: "bottom",
|
||||
stacked: false,
|
||||
gridLines: {
|
||||
drawOnChartArea: false,
|
||||
drawBorder: false,
|
||||
drawTicks: false
|
||||
},
|
||||
ticks: {
|
||||
fontSize:10,
|
||||
fontColor: "#939ba1",
|
||||
padding:16
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
stacked: false,
|
||||
gridLines: {
|
||||
borderDash: [5,3],
|
||||
lineWidth: 1,
|
||||
drawBorder: false,
|
||||
drawTicks: false,
|
||||
color: "#33939ba1"
|
||||
},
|
||||
ticks: {
|
||||
fontSize: 10,
|
||||
fontColor: "#939ba1",
|
||||
padding: 8,
|
||||
maxTicksLimit: 10,
|
||||
beginAtZero: true,
|
||||
stepSize: 1
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
labels:["16:40","16:50","17:00","17:10","17:20","17:30"],
|
||||
datasets: [{
|
||||
xAxisId: "x-axis-1",
|
||||
yAxisId: "y-axis-1",
|
||||
backgroundColor: "#334360df",
|
||||
pointRadius: 0,
|
||||
hoverBackgroundColor: "#334360df",
|
||||
hoverBorderColor: "#4360df",
|
||||
hoverBorderWidth: 2,
|
||||
data: [8,3,5,4,3,10]
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var crosshairConfig: {
|
||||
//binding to regenerate data and reset zoom
|
||||
chartType.currentText
|
||||
const generateDataset = (shift, label, color) => {
|
||||
var data = [];
|
||||
var x = 0;
|
||||
|
||||
while (x < 30) {
|
||||
data.push({ x: x, y: Math.sin(shift + x / 3) });
|
||||
x += Math.random();
|
||||
}
|
||||
|
||||
var dataset = {
|
||||
backgroundColor: color,
|
||||
borderColor: color,
|
||||
showLine: true,
|
||||
fill: false,
|
||||
pointRadius: 2,
|
||||
label: label,
|
||||
data: data,
|
||||
lineTension: 0,
|
||||
interpolate: true
|
||||
};
|
||||
return dataset;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "scatter",
|
||||
options: {
|
||||
plugins: {
|
||||
crosshair: {
|
||||
enabled: true,
|
||||
sync: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltips: {
|
||||
mode: "interpolate",
|
||||
intersect: true,
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x-axis-1',
|
||||
}],
|
||||
yAxes: [{
|
||||
position: 'left',
|
||||
id: 'y-axis-1',
|
||||
}]
|
||||
}
|
||||
},
|
||||
data: {
|
||||
datasets: [
|
||||
generateDataset(0, "A", "red"),
|
||||
generateDataset(1, "B", "green"),
|
||||
generateDataset(2, "C", "blue")
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
readonly property var minimisedConfig: {
|
||||
let config = Object.assign({}, d.lineConfig)
|
||||
config.datasets = [{
|
||||
label: 'Price',
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: "transparent",
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: 3,
|
||||
pointRadius: 0,
|
||||
data: d.generateData()
|
||||
}]
|
||||
config.options = Object.assign({}, d.lineConfig.options)
|
||||
|
||||
config.options.scales = {
|
||||
xAxes: [{
|
||||
id: 'x-axis-1',
|
||||
display: false
|
||||
}],
|
||||
yAxes: [{
|
||||
id: 'y-axis-1',
|
||||
display: false
|
||||
}]
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
readonly property var dataLabelsConfig: {
|
||||
|
||||
var DATA_COUNT = 8;
|
||||
var labels = [];
|
||||
|
||||
for (var i = 0; i < DATA_COUNT; ++i) {
|
||||
labels.push('' + i);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'line',
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
backgroundColor: "blue",
|
||||
borderColor: "green",
|
||||
data: [5, 10, 15, 10, 5, 0, 5, 10],
|
||||
datalabels: {
|
||||
align: 'start',
|
||||
anchor: 'start'
|
||||
}
|
||||
}, {
|
||||
backgroundColor:"red",
|
||||
borderColor:"orance",
|
||||
data: [58, 80, 60, 70, 50, 60, 70, 80],
|
||||
}, {
|
||||
backgroundColor: "yellow",
|
||||
borderColor: "green",
|
||||
data: [30, 40, 30, 40, 30, 40, 30, 40],
|
||||
datalabels: {
|
||||
align: 'end',
|
||||
anchor: 'end'
|
||||
}
|
||||
}],
|
||||
options: {
|
||||
plugins: {
|
||||
datalabels: {
|
||||
backgroundColor: "red",
|
||||
borderRadius: 4,
|
||||
color: 'white',
|
||||
font: {
|
||||
weight: 'bold',
|
||||
size: 12
|
||||
},
|
||||
formatter: Math.round,
|
||||
padding: 6
|
||||
}
|
||||
},
|
||||
|
||||
// Core options
|
||||
aspectRatio: 5 / 3,
|
||||
layout: {
|
||||
padding: {
|
||||
top: 32,
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
left: 8
|
||||
}
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
fill: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
stacked: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusMenu {
|
||||
id: toolTip
|
||||
width: 243 //By design
|
||||
topPadding: Style.current.padding
|
||||
bottomPadding: topPadding
|
||||
leftPadding: topPadding
|
||||
rightPadding: topPadding
|
||||
parent: Overlay.overlay
|
||||
property alias text: label.text
|
||||
Label {
|
||||
id: label
|
||||
text: "Tooltip"
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
StatusChartPanel {
|
||||
id: graphDetail
|
||||
height: 290
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 24
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
graphsModel: d.graphTabsModel
|
||||
timeRangeModel: d.timeRangeTabsModel
|
||||
chart.type: d.lineConfig.type
|
||||
chart.labels: d.lineConfig.labels
|
||||
chart.datasets: d.lineConfig.datasets
|
||||
chart.options: d.lineConfig.options
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
|
@ -204,6 +431,49 @@ SplitView {
|
|||
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 150
|
||||
|
||||
ComboBox {
|
||||
id: chartType
|
||||
model: d.lineConfig ? ["line", "bar", "with crosshair", "line minimised", "data labels"] : []
|
||||
currentIndex: 0
|
||||
onCurrentTextChanged: {
|
||||
if (chartType.currentText === "line") {
|
||||
graphDetail.chart.type = d.lineConfig.type;
|
||||
graphDetail.chart.labels = d.lineConfig.labels;
|
||||
graphDetail.chart.datasets = d.lineConfig.datasets;
|
||||
graphDetail.chart.options = d.lineConfig.options;
|
||||
graphDetail.chart.plugins = []
|
||||
graphDetail.chart.rebuild();
|
||||
} else if (chartType.currentText === "bar") {
|
||||
graphDetail.chart.type = d.barConfig.type;
|
||||
graphDetail.chart.labels = d.barConfig.labels;
|
||||
graphDetail.chart.datasets = d.barConfig.datasets;
|
||||
graphDetail.chart.options = d.barConfig.options;
|
||||
graphDetail.chart.plugins = []
|
||||
graphDetail.chart.rebuild();
|
||||
} else if (chartType.currentText === "with crosshair") {
|
||||
graphDetail.chart.type = d.crosshairConfig.type;
|
||||
graphDetail.chart.options = d.crosshairConfig.options;
|
||||
graphDetail.chart.datasets = d.crosshairConfig.data.datasets;
|
||||
graphDetail.chart.plugins = []
|
||||
graphDetail.chart.rebuild();
|
||||
} else if (chartType.currentText === "line minimised") {
|
||||
graphDetail.chart.type = d.minimisedConfig.type;
|
||||
graphDetail.chart.labels = d.minimisedConfig.labels;
|
||||
graphDetail.chart.datasets = d.minimisedConfig.datasets;
|
||||
graphDetail.chart.options = d.minimisedConfig.options;
|
||||
graphDetail.chart.plugins = []
|
||||
graphDetail.chart.rebuild();
|
||||
} else if (chartType.currentText === "data labels") {
|
||||
graphDetail.chart.type = d.dataLabelsConfig.type;
|
||||
graphDetail.chart.datasets = d.dataLabelsConfig.datasets;
|
||||
graphDetail.chart.labels = d.dataLabelsConfig.labels;
|
||||
graphDetail.chart.options = d.dataLabelsConfig.options;
|
||||
graphDetail.chart.plugins = [graphDetail.chart.availablePlugins.datalabels]
|
||||
graphDetail.chart.rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,8 +75,8 @@ Item {
|
|||
for (var i = 0; i < timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange].length; ++i) {
|
||||
result[i] = Math.random() * (maxStep - minStep) + minStep;
|
||||
}
|
||||
graphDetail.chart.chartData.datasets[0].data = result;
|
||||
graphDetail.chart.animateToNewData();
|
||||
graphDetail.chart.datasets[0].data = result;
|
||||
graphDetail.chart.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,24 +97,21 @@ Item {
|
|||
//if graph bar
|
||||
//switch graph
|
||||
}
|
||||
chart.chartType: 'line'
|
||||
chart.chartData: {
|
||||
return {
|
||||
labels: d.timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange],
|
||||
datasets: [{
|
||||
label: 'Price',
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: 3,
|
||||
pointRadius: 0,
|
||||
//data: d.generateData()
|
||||
}]
|
||||
}
|
||||
chart.type: 'line'
|
||||
chart.labels: d.timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange]
|
||||
chart.datasets: {
|
||||
return [{
|
||||
label: 'Price',
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: 3,
|
||||
pointRadius: 0
|
||||
}]
|
||||
}
|
||||
|
||||
chart.chartOptions: {
|
||||
chart.options: {
|
||||
return {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
|
|
|
@ -170,7 +170,7 @@ Page {
|
|||
}
|
||||
|
||||
contentItem: Item {
|
||||
Chart {
|
||||
ChartCanvas {
|
||||
id: graphComponent
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
/*!
|
||||
* Elypson's Chart.qml adaptor to Chart.js
|
||||
* (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
import QtQuick 2.13
|
||||
import "Chart.js" as Chart
|
||||
|
||||
Canvas {
|
||||
id: root
|
||||
|
||||
property var jsChart: undefined
|
||||
property string chartType
|
||||
property var chartData
|
||||
property var chartOptions
|
||||
property double chartAnimationProgress: 0.1
|
||||
property var animationEasingType: Easing.InOutExpo
|
||||
property double animationDuration: 500
|
||||
property var memorizedContext
|
||||
property var memorizedData
|
||||
property var memorizedOptions
|
||||
property alias animationRunning: chartAnimator.running
|
||||
|
||||
signal animationFinished()
|
||||
|
||||
|
||||
function updateToNewData()
|
||||
{
|
||||
if(!jsChart) return
|
||||
|
||||
jsChart.update('none');
|
||||
root.requestPaint();
|
||||
}
|
||||
|
||||
function animateToNewData()
|
||||
{
|
||||
chartAnimationProgress = 0.1;
|
||||
jsChart.update();
|
||||
chartAnimator.restart();
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: event
|
||||
anchors.fill: root
|
||||
hoverEnabled: true
|
||||
enabled: true
|
||||
property var handler: undefined
|
||||
|
||||
property QtObject mouseEvent: QtObject {
|
||||
property int left: 0
|
||||
property int top: 0
|
||||
property int x: 0
|
||||
property int y: 0
|
||||
property int clientX: 0
|
||||
property int clientY: 0
|
||||
property string type: ""
|
||||
property var target
|
||||
}
|
||||
|
||||
function submitEvent(mouse, type) {
|
||||
mouseEvent.type = type
|
||||
mouseEvent.clientX = mouse ? mouse.x : 0;
|
||||
mouseEvent.clientY = mouse ? mouse.y : 0;
|
||||
mouseEvent.x = mouse ? mouse.x : 0;
|
||||
mouseEvent.y = mouse ? mouse.y : 0;
|
||||
mouseEvent.left = 0;
|
||||
mouseEvent.top = 0;
|
||||
mouseEvent.target = root;
|
||||
|
||||
if(handler) {
|
||||
handler(mouseEvent);
|
||||
}
|
||||
|
||||
root.requestPaint();
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
submitEvent(mouse, "click");
|
||||
}
|
||||
onPositionChanged: {
|
||||
submitEvent(mouse, "mousemove");
|
||||
}
|
||||
onExited: {
|
||||
submitEvent(undefined, "mouseout");
|
||||
}
|
||||
onEntered: {
|
||||
submitEvent(undefined, "mouseenter");
|
||||
}
|
||||
onPressed: {
|
||||
submitEvent(mouse, "mousedown");
|
||||
}
|
||||
onReleased: {
|
||||
submitEvent(mouse, "mouseup");
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAnimation {
|
||||
id: chartAnimator
|
||||
target: root
|
||||
property: "chartAnimationProgress"
|
||||
alwaysRunToEnd: true
|
||||
to: 1
|
||||
duration: root.animationDuration
|
||||
easing.type: root.animationEasingType
|
||||
onFinished: {
|
||||
root.animationFinished();
|
||||
}
|
||||
}
|
||||
|
||||
onChartAnimationProgressChanged: {
|
||||
root.requestPaint();
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
if(root.getContext('2d') != null && memorizedContext != root.getContext('2d') || memorizedData != root.chartData || memorizedOptions != root.chartOptions) {
|
||||
var ctx = root.getContext('2d');
|
||||
|
||||
jsChart = new Chart.build(ctx, {
|
||||
type: root.chartType,
|
||||
data: root.chartData,
|
||||
options: root.chartOptions
|
||||
});
|
||||
|
||||
memorizedData = root.chartData ;
|
||||
memorizedContext = root.getContext('2d');
|
||||
memorizedOptions = root.chartOptions;
|
||||
|
||||
root.jsChart.bindEvents(function(newHandler) {event.handler = newHandler;});
|
||||
|
||||
chartAnimator.start();
|
||||
}
|
||||
|
||||
jsChart.draw(chartAnimationProgress);
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
if(jsChart) {
|
||||
jsChart.resize();
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: {
|
||||
if(jsChart) {
|
||||
jsChart.resize();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import "./Library/Library.js" as Lib
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
|
||||
readonly property var availablePlugins: {
|
||||
return { datalabels: ChartDataLabels }
|
||||
}
|
||||
|
||||
property string type: chartType
|
||||
property var options: chartOptions
|
||||
property var plugins: []
|
||||
property var labels: []
|
||||
property var datasets: []
|
||||
|
||||
signal resized()
|
||||
|
||||
function refresh() {
|
||||
if (d.instance) {
|
||||
Qt.callLater(d.refresh)
|
||||
}
|
||||
}
|
||||
|
||||
function rebuild() {
|
||||
if (available) {
|
||||
Qt.callLater(d.rebuild)
|
||||
}
|
||||
}
|
||||
|
||||
// [WORKAROUND] context.lineWidth > 1 makes the scene graph polish step very slow
|
||||
// in case of "Image" render target, so by default let's draw with OpenGL when
|
||||
// possible (which seems only possible with "Cooperative" strategy).
|
||||
renderTarget: Canvas.FramebufferObject
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
// https://www.w3.org/TR/2012/WD-html5-author-20120329/the-canvas-element.html#the-canvas-element
|
||||
implicitHeight: 150
|
||||
implicitWidth: 300
|
||||
|
||||
// [polyfill] Element
|
||||
readonly property alias clientHeight: canvas.height
|
||||
readonly property alias clientWidth: canvas.width
|
||||
|
||||
// [polyfill] canvas.style
|
||||
readonly property var style: ({
|
||||
height: canvas.height,
|
||||
width: canvas.width
|
||||
})
|
||||
|
||||
// [polyfill] element.getBoundingClientRect
|
||||
// https://developer.mozilla.org/docs/Web/API/Element/getBoundingClientRect
|
||||
function getBoundingClientRect() {
|
||||
return {top: 0, right: canvas.width, bottom: canvas.height, left: 0}
|
||||
}
|
||||
|
||||
/**
|
||||
\internal object used to forward events to the Chart.js instance
|
||||
\see Library.js for the list of events
|
||||
*/
|
||||
property QtObject _eventSource: QtObject {
|
||||
signal resized(var event)
|
||||
signal clicked(var event)
|
||||
signal positionChanged(var event)
|
||||
signal entered(var event)
|
||||
signal exited(var event)
|
||||
signal pressed(var event)
|
||||
signal released(var event)
|
||||
|
||||
property var connectedHandlers: []
|
||||
|
||||
readonly property Connections canvasConn: Connections {
|
||||
target: canvas
|
||||
function onResized() {
|
||||
_eventSource.resized(null)
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Connections mouseConn: Connections {
|
||||
target: mouse
|
||||
function onPositionChanged(event) {
|
||||
_eventSource.positionChanged(event)
|
||||
}
|
||||
function onEntered(event) {
|
||||
_eventSource.entered(event)
|
||||
}
|
||||
function onExited(event) {
|
||||
_eventSource.exited(event)
|
||||
}
|
||||
function onPressed(event) {
|
||||
_eventSource.pressed(event)
|
||||
}
|
||||
function onReleased(event) {
|
||||
_eventSource.released(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: enabled
|
||||
}
|
||||
|
||||
onTypeChanged: rebuild()
|
||||
onOptionsChanged: refresh()
|
||||
onPluginsChanged: refresh()
|
||||
onLabelsChanged: refresh()
|
||||
onDatasetsChanged: rebuild()
|
||||
onHeightChanged: resized()
|
||||
onWidthChanged: resized()
|
||||
onAvailableChanged: {
|
||||
if (!d.instance) {
|
||||
rebuild()
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property var instance: null
|
||||
|
||||
function refresh() {
|
||||
instance.config.options = canvas.options
|
||||
instance.config.plugins = canvas.plugins
|
||||
instance.data.labels = canvas.labels
|
||||
instance.update()
|
||||
}
|
||||
|
||||
function rebuild() {
|
||||
if (instance) {
|
||||
instance.destroy()
|
||||
instance = null
|
||||
}
|
||||
var ctx = canvas.getContext('2d');
|
||||
const config = {
|
||||
type: canvas.type,
|
||||
options: canvas.options,
|
||||
plugins: canvas.plugins,
|
||||
data: {
|
||||
labels: canvas.labels,
|
||||
datasets: canvas.datasets
|
||||
}
|
||||
}
|
||||
instance = new Chart(ctx, config)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (d.instance) {
|
||||
d.instance.destroy()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,58 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
property double chartAnimationProgress: 0.1
|
||||
property int chartAnimationDuration: 500
|
||||
property var animationEasingType: Easing.InOutExpo
|
||||
|
||||
property var _requests: []
|
||||
|
||||
signal animationFinished()
|
||||
|
||||
readonly property PropertyAnimation animator : PropertyAnimation {
|
||||
id: chartAnimator
|
||||
target: root
|
||||
property: "chartAnimationProgress"
|
||||
alwaysRunToEnd: true
|
||||
to: 1
|
||||
duration: root.chartAnimationDuration
|
||||
easing.type: root.animationEasingType
|
||||
onFinished: {
|
||||
root.chartAnimationProgress = 0.1
|
||||
root.animationFinished()
|
||||
}
|
||||
}
|
||||
|
||||
onChartAnimationProgressChanged: {
|
||||
root.animate();
|
||||
}
|
||||
|
||||
function requestAnimation(callback) {
|
||||
_requests.push({
|
||||
callback: callback,
|
||||
scope: this
|
||||
})
|
||||
|
||||
if (!chartAnimator.running) {
|
||||
root.chartAnimationProgress = 0.1
|
||||
chartAnimator.restart();
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
function animate() {
|
||||
var requests = _requests
|
||||
var ilen = requests.length
|
||||
|
||||
var requestItem = null
|
||||
var i = 0
|
||||
|
||||
_requests = []
|
||||
for (; i < ilen; ++i) {
|
||||
requestItem = requests[i]
|
||||
requestItem.callback.call(requestItem.scope)
|
||||
}
|
||||
}
|
||||
}
|
41577
ui/StatusQ/src/StatusQ/Components/private/chart/Chart.js → ui/StatusQ/src/StatusQ/Components/private/chart/Library/Chart.bundle.js
Normal file → Executable file
41577
ui/StatusQ/src/StatusQ/Components/private/chart/Chart.js → ui/StatusQ/src/StatusQ/Components/private/chart/Library/Chart.bundle.js
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,141 @@
|
|||
.import "Polyfills.js" as Polyfills
|
||||
.import "Chart.bundle.js" as ChartJs
|
||||
.import "chartjs-plugin-crosshair.js" as CrosshairLib
|
||||
.import "chartjs-plugin-datalabels.js" as DataLabelsLib
|
||||
|
||||
.import StatusQ.Core.Theme 0.1 as SQTheme
|
||||
|
||||
|
||||
/*!
|
||||
/This file is used to provide the necessary wrappers for Chart.js to work in QML
|
||||
/It is loaded after Chart.js and plugins and provides the necessary functions
|
||||
/NOTE: Some plugins work out of the box, others need to be adapted to work in QML
|
||||
!*/
|
||||
|
||||
(function(global){
|
||||
Chart.helpers.merge(Chart.defaults.global, {
|
||||
// Default options
|
||||
events: [
|
||||
"mousemove",
|
||||
"mouseout",
|
||||
"click"
|
||||
]
|
||||
})
|
||||
|
||||
// QML rendering plugin
|
||||
Chart.plugins.register({
|
||||
afterDraw: function(chart) {
|
||||
chart.canvas.requestPaint()
|
||||
}
|
||||
})
|
||||
|
||||
var EVENTS = {
|
||||
/*chartJS event: QML event*/
|
||||
click: "clicked",
|
||||
mousemove: "positionChanged",
|
||||
mouseenter: "entered",
|
||||
mouseout: "exited",
|
||||
mousedown: "pressed",
|
||||
mouseup: "released",
|
||||
resize: "resized"
|
||||
}
|
||||
|
||||
function createEvent(type, chart, x, y, native, target) {
|
||||
return {
|
||||
type: type,
|
||||
chart: chart,
|
||||
native: native || null,
|
||||
x: x !== undefined ? x : null,
|
||||
y: y !== undefined ? y : null,
|
||||
target: target || null,
|
||||
}
|
||||
}
|
||||
|
||||
// QML platform implementation
|
||||
Chart.helpers.merge(Chart.platform, {
|
||||
addEventListener: function(chart, type, listener) {
|
||||
const mapped = EVENTS[type]
|
||||
if (!mapped) {
|
||||
console.warn("Unsupported event:", type)
|
||||
return
|
||||
}
|
||||
|
||||
const canvas = chart.canvas
|
||||
const qmlHandler = (event) => {
|
||||
listener(createEvent(
|
||||
type,
|
||||
chart,
|
||||
event && event.x,
|
||||
event && event.y,
|
||||
event,
|
||||
canvas))
|
||||
}
|
||||
|
||||
canvas._eventSource[mapped].connect(qmlHandler)
|
||||
canvas._eventSource.connectedHandlers[listener] = qmlHandler
|
||||
},
|
||||
|
||||
removeEventListener: function(chart, type, listener) {
|
||||
const canvas = chart.canvas
|
||||
if (!canvas._eventSource.connectedHandlers[listener]) {
|
||||
return
|
||||
}
|
||||
|
||||
const mapped = EVENTS[type]
|
||||
const qmlHandler = canvas._eventSource.connectedHandlers[listener]
|
||||
canvas._eventSource[mapped].disconnect(qmlHandler)
|
||||
delete canvas._eventSource.connectedHandlers[listener]
|
||||
}
|
||||
})
|
||||
|
||||
Chart.helpers.merge(Chart.helpers, {
|
||||
color: function(c) {
|
||||
return Color(c)
|
||||
},
|
||||
getHoverColor: function(c) {
|
||||
const hoverColor = SQTheme.Theme.palette.hoverColor(c)
|
||||
if (!hoverColor) {
|
||||
return Color(c)
|
||||
}
|
||||
|
||||
return hoverColor
|
||||
}
|
||||
})
|
||||
|
||||
Chart.helpers.merge(Chart.prototype, {
|
||||
// Resync chart internals with current canvas size, then update
|
||||
resize: function(silent) {
|
||||
var me = this
|
||||
if (!me.canvas) {
|
||||
return
|
||||
}
|
||||
|
||||
var opts = me.options
|
||||
var h = Math.max(0, me.canvas.height)
|
||||
var w = Math.max(0, me.canvas.width)
|
||||
|
||||
if (h === me.height && w === me.width) {
|
||||
return
|
||||
}
|
||||
|
||||
me.height = h
|
||||
me.width = w
|
||||
|
||||
if (silent) {
|
||||
return
|
||||
}
|
||||
|
||||
// Notify any plugins about the resize
|
||||
var size = {width: w, height: h}
|
||||
Chart.plugins.notify(me, "resize", [size])
|
||||
|
||||
// Notify of resize
|
||||
if (opts.onResize) {
|
||||
opts.onResize(me, newSize)
|
||||
}
|
||||
|
||||
me.stop()
|
||||
me.update(opts.responsiveAnimationDuration)
|
||||
}
|
||||
})
|
||||
})(this)
|
|
@ -0,0 +1,23 @@
|
|||
.pragma library
|
||||
|
||||
/*!
|
||||
/This file is used to provide the necessary polyfills for Chart.js to work in QML
|
||||
/It is loaded before Chart.js and fills the global object with the necessary functions
|
||||
/to make Chart.js work in QML.
|
||||
|
||||
/The following polyfills are provided:
|
||||
/- requestAnimationFrame https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
|
||||
/- window https://developer.mozilla.org/en-US/docs/Web/API/window
|
||||
|
||||
!*/
|
||||
|
||||
(function(global){
|
||||
// ChartJs needs a global object to work. Simulating the window object
|
||||
global.window = global
|
||||
|
||||
var _animator = Qt.createComponent("Animator.qml").createObject()
|
||||
// TODO: Find a way to use the canvas `requestAnimation`
|
||||
global.requestAnimationFrame = function(callback) {
|
||||
return _animator.requestAnimation(callback)
|
||||
}
|
||||
})(this)
|
|
@ -0,0 +1,702 @@
|
|||
/*
|
||||
* @license
|
||||
* chartjs-plugin-crosshair
|
||||
* http://abelheinsbroek.nl/
|
||||
* Version: 1.1.5
|
||||
*
|
||||
* Copyright 2024 Abel Heinsbroek
|
||||
* Released under the MIT license
|
||||
* https://github.com/abelheinsbroek/chartjs-plugin-crosshair/blob/master/LICENSE.md
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
|
||||
(factory(global.Chart));
|
||||
}(this, (function (Chart) { 'use strict';
|
||||
|
||||
Chart = Chart && Chart.hasOwnProperty('default') ? Chart['default'] : Chart;
|
||||
|
||||
var Interpolate = function(Chart$$1) {
|
||||
|
||||
Chart$$1.Interaction.modes.interpolate = function(chart, e, options) {
|
||||
|
||||
|
||||
var items = [];
|
||||
|
||||
for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
|
||||
|
||||
|
||||
// check for interpolate setting
|
||||
if (!chart.data.datasets[datasetIndex].interpolate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var meta = chart.getDatasetMeta(datasetIndex);
|
||||
// do not interpolate hidden charts
|
||||
if (meta.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var xScale = chart.scales[meta.xAxisID];
|
||||
var yScale = chart.scales[meta.yAxisID];
|
||||
if (!xScale || !yScale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var xValue = xScale.getValueForPixel(e.x);
|
||||
|
||||
|
||||
var data = chart.data.datasets[datasetIndex].data;
|
||||
|
||||
var index = data.findIndex(function(o) {
|
||||
return o.x >= xValue;
|
||||
});
|
||||
|
||||
if (index === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// linear interpolate value
|
||||
var prev = data[index - 1];
|
||||
var next = data[index];
|
||||
|
||||
if (prev && next) {
|
||||
var slope = (next.y - prev.y) / (next.x - prev.x);
|
||||
var interpolatedValue = prev.y + (xValue - prev.x) * slope;
|
||||
}
|
||||
|
||||
if (chart.data.datasets[datasetIndex].steppedLine && prev) {
|
||||
interpolatedValue = prev.y;
|
||||
}
|
||||
|
||||
if (isNaN(interpolatedValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var yPosition = yScale.getPixelForValue(interpolatedValue);
|
||||
|
||||
// do not interpolate values outside of the axis limits
|
||||
if (isNaN(yPosition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create a 'fake' event point
|
||||
|
||||
var fakePoint = {
|
||||
|
||||
value: interpolatedValue,
|
||||
xValue: xValue,
|
||||
|
||||
tooltipPosition: function() {
|
||||
return this._model;
|
||||
},
|
||||
hasValue: function() {
|
||||
return true;
|
||||
},
|
||||
_model: {
|
||||
x: e.x,
|
||||
y: yPosition
|
||||
},
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: items.length,
|
||||
_xScale: {
|
||||
getLabelForIndex: function(indx) {
|
||||
return items[indx].xValue;
|
||||
}
|
||||
},
|
||||
_yScale: {
|
||||
getLabelForIndex: function(indx) {
|
||||
return items[indx].value;
|
||||
}
|
||||
},
|
||||
_chart: chart
|
||||
};
|
||||
|
||||
items.push(fakePoint);
|
||||
}
|
||||
|
||||
|
||||
// add other, not interpolated, items
|
||||
var xItems = Chart$$1.Interaction.modes.x(chart, e, options);
|
||||
for (index = 0; index < xItems.length; index++) {
|
||||
var item = xItems[index];
|
||||
if (!chart.data.datasets[item._datasetIndex].interpolate) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
};
|
||||
|
||||
var TracePlugin = function(Chart$$1) {
|
||||
var helpers = Chart$$1.helpers;
|
||||
|
||||
var defaultOptions = {
|
||||
enabled: false,
|
||||
line: {
|
||||
color: '#F66',
|
||||
width: 1,
|
||||
dashPattern: []
|
||||
},
|
||||
sync: {
|
||||
enabled: false,
|
||||
group: 1,
|
||||
suppressTooltips: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: true,
|
||||
zoomboxBackgroundColor: 'rgba(66,133,244,0.2)',
|
||||
zoomboxBorderColor: '#48F',
|
||||
zoomButtonText: 'Reset Zoom',
|
||||
zoomButtonClass: 'reset-zoom',
|
||||
},
|
||||
snap: {
|
||||
enabled: false,
|
||||
},
|
||||
callbacks: {
|
||||
beforeZoom: function(start, end) {
|
||||
return true;
|
||||
},
|
||||
afterZoom: function(start, end) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var crosshairPlugin = {
|
||||
|
||||
id: 'crosshair',
|
||||
|
||||
afterInit: function(chart) {
|
||||
if (!chart.config.options.scales || chart.config.options.scales.xAxes.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var xScaleType = chart.config.options.scales.xAxes[0].type;
|
||||
|
||||
if (xScaleType !== 'linear' && xScaleType !== 'time' && xScaleType !== 'category' && xscaleType !== 'logarithmic') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chart.options.plugins.crosshair === undefined || chart.options.plugins.crosshair.enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
chart.crosshair = {
|
||||
enabled: false,
|
||||
x: null,
|
||||
originalData: [],
|
||||
originalXRange: {},
|
||||
dragStarted: false,
|
||||
dragStartX: null,
|
||||
dragEndX: null,
|
||||
suppressTooltips: false,
|
||||
reset: function() {
|
||||
this.resetZoom(chart, false, false);
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
var syncEnabled = this.getOption(chart, 'sync', 'enabled');
|
||||
if (syncEnabled) {
|
||||
chart.crosshair.syncEventHandler = function(e) {
|
||||
this.handleSyncEvent(chart, e);
|
||||
}.bind(this);
|
||||
|
||||
chart.crosshair.resetZoomEventHandler = function(e) {
|
||||
|
||||
var syncGroup = this.getOption(chart, 'sync', 'group');
|
||||
|
||||
if (e.chartId !== chart.id && e.syncGroup === syncGroup) {
|
||||
this.resetZoom(chart, true);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
window.addEventListener('sync-event', chart.crosshair.syncEventHandler);
|
||||
window.addEventListener('reset-zoom-event', chart.crosshair.resetZoomEventHandler);
|
||||
}
|
||||
|
||||
chart.panZoom = this.panZoom.bind(this, chart);
|
||||
},
|
||||
|
||||
destroy: function(chart) {
|
||||
if (!chart.crosshair){
|
||||
return;
|
||||
}
|
||||
|
||||
var syncEnabled = this.getOption(chart, 'sync', 'enabled');
|
||||
if (syncEnabled) {
|
||||
window.removeEventListener('sync-event', chart.crosshair.syncEventHandler);
|
||||
window.removeEventListener('reset-zoom-event', chart.crosshair.resetZoomEventHandler);
|
||||
}
|
||||
},
|
||||
|
||||
panZoom: function(chart, increment) {
|
||||
if (chart.crosshair.originalData.length === 0) {
|
||||
return;
|
||||
}
|
||||
var diff = chart.crosshair.end - chart.crosshair.start;
|
||||
var min = chart.crosshair.min;
|
||||
var max = chart.crosshair.max;
|
||||
if (increment < 0) { // left
|
||||
chart.crosshair.start = Math.max(chart.crosshair.start + increment, min);
|
||||
chart.crosshair.end = chart.crosshair.start === min ? min + diff : chart.crosshair.end + increment;
|
||||
} else { // right
|
||||
chart.crosshair.end = Math.min(chart.crosshair.end + increment, chart.crosshair.max);
|
||||
chart.crosshair.start = chart.crosshair.end === max ? max - diff : chart.crosshair.start + increment;
|
||||
}
|
||||
|
||||
this.doZoom(chart, chart.crosshair.start, chart.crosshair.end);
|
||||
},
|
||||
|
||||
getOption: function(chart, category, name) {
|
||||
if (!chart.crosshair) {
|
||||
return;
|
||||
}
|
||||
return helpers.getValueOrDefault(chart.options.plugins.crosshair[category] ? chart.options.plugins.crosshair[category][name] : undefined, defaultOptions[category][name]);
|
||||
},
|
||||
|
||||
getXScale: function(chart) {
|
||||
return chart.data.datasets.length ? chart.scales[chart.getDatasetMeta(0).xAxisID] : null;
|
||||
},
|
||||
getYScale: function(chart) {
|
||||
return chart.scales[chart.getDatasetMeta(0).yAxisID];
|
||||
},
|
||||
|
||||
handleSyncEvent: function(chart, e) {
|
||||
|
||||
var syncGroup = this.getOption(chart, 'sync', 'group');
|
||||
|
||||
// stop if the sync event was fired from this chart
|
||||
if (e.chartId === chart.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// stop if the sync event was fired from a different group
|
||||
if (e.syncGroup !== syncGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
var xScale = this.getXScale(chart);
|
||||
|
||||
if (!xScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safari fix
|
||||
var buttons = (e.original.native.buttons === undefined ? e.original.native.which : e.original.native.buttons);
|
||||
if (e.original.type === 'mouseup') {
|
||||
buttons = 0;
|
||||
}
|
||||
|
||||
var newEvent = {
|
||||
type: e.original.type,
|
||||
chart: chart,
|
||||
x: xScale.getPixelForValue(e.xValue),
|
||||
y: e.original.y,
|
||||
native: {
|
||||
buttons: buttons
|
||||
},
|
||||
stop: true
|
||||
};
|
||||
chart.controller.eventHandler(newEvent);
|
||||
},
|
||||
|
||||
afterEvent: function(chart, e) {
|
||||
|
||||
if (!chart.crosshair) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chart.config.options.scales.xAxes.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var xScaleType = chart.config.options.scales.xAxes[0].type;
|
||||
|
||||
if (xScaleType !== 'linear' && xScaleType !== 'time' && xScaleType !== 'category' && xscaleType !== 'logarithmic') {
|
||||
return;
|
||||
}
|
||||
|
||||
var xScale = this.getXScale(chart);
|
||||
|
||||
|
||||
if (!xScale) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// fix for Safari
|
||||
var buttons = e.native ? (e.native.buttons === undefined ? e.native.which : e.native.buttons) : 0;
|
||||
if (e.native && e.native.type === 'mouseup') {
|
||||
buttons = 0;
|
||||
}
|
||||
|
||||
var syncEnabled = this.getOption(chart, 'sync', 'enabled');
|
||||
var syncGroup = this.getOption(chart, 'sync', 'group');
|
||||
|
||||
// fire event for all other linked charts
|
||||
if (!e.stop && syncEnabled) {
|
||||
var event = new CustomEvent('sync-event');
|
||||
event.chartId = chart.id;
|
||||
event.syncGroup = syncGroup;
|
||||
event.original = e;
|
||||
event.xValue = xScale.getValueForPixel(e.x);
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
||||
// suppress tooltips for linked charts
|
||||
var suppressTooltips = this.getOption(chart, 'sync', 'suppressTooltips');
|
||||
|
||||
chart.crosshair.suppressTooltips = e.stop && suppressTooltips;
|
||||
|
||||
chart.crosshair.enabled = (e.type !== 'mouseout' && (e.x > xScale.getPixelForValue(xScale.min) && e.x < xScale.getPixelForValue(xScale.max)));
|
||||
|
||||
if (!chart.crosshair.enabled) {
|
||||
if (e.x > xScale.getPixelForValue(xScale.max)) {
|
||||
chart.update();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle drag to zoom
|
||||
var zoomEnabled = this.getOption(chart, 'zoom', 'enabled');
|
||||
|
||||
if (buttons === 1 && !chart.crosshair.dragStarted && zoomEnabled) {
|
||||
chart.crosshair.dragStartX = e.x;
|
||||
chart.crosshair.dragStarted = true;
|
||||
}
|
||||
|
||||
// handle drag to zoom
|
||||
if (chart.crosshair.dragStarted && buttons === 0) {
|
||||
chart.crosshair.dragStarted = false;
|
||||
|
||||
var start = xScale.getValueForPixel(chart.crosshair.dragStartX);
|
||||
var end = xScale.getValueForPixel(chart.crosshair.x);
|
||||
|
||||
if (Math.abs(chart.crosshair.dragStartX - chart.crosshair.x) > 1) {
|
||||
this.doZoom(chart, start, end);
|
||||
}
|
||||
chart.update();
|
||||
}
|
||||
|
||||
chart.crosshair.x = e.x;
|
||||
|
||||
|
||||
chart.draw();
|
||||
|
||||
},
|
||||
|
||||
afterDraw: function(chart) {
|
||||
|
||||
if (!chart.crosshair) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chart.crosshair.dragStarted) {
|
||||
this.drawZoombox(chart);
|
||||
} else {
|
||||
this.drawTraceLine(chart);
|
||||
this.interpolateValues(chart);
|
||||
this.drawTracePoints(chart);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
beforeTooltipDraw: function(chart) {
|
||||
// suppress tooltips on dragging
|
||||
if (!chart.crosshair) {
|
||||
return;
|
||||
}
|
||||
return !chart.crosshair.dragStarted && !chart.crosshair.suppressTooltips;
|
||||
},
|
||||
|
||||
resetZoom: function(chart) {
|
||||
var stop = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
||||
var update = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
||||
|
||||
if (update) {
|
||||
for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
|
||||
var dataset = chart.data.datasets[datasetIndex];
|
||||
dataset.data = chart.crosshair.originalData.shift(0);
|
||||
}
|
||||
|
||||
var range = 'ticks';
|
||||
|
||||
if (chart.options.scales.xAxes[0].time) {
|
||||
range = 'time';
|
||||
}
|
||||
// reset original xRange
|
||||
if (chart.crosshair.originalXRange.min) {
|
||||
chart.options.scales.xAxes[0][range].min = chart.crosshair.originalXRange.min;
|
||||
chart.crosshair.originalXRange.min = null;
|
||||
} else {
|
||||
delete chart.options.scales.xAxes[0][range].min;
|
||||
}
|
||||
if (chart.crosshair.originalXRange.max) {
|
||||
chart.options.scales.xAxes[0][range].max = chart.crosshair.originalXRange.max;
|
||||
chart.crosshair.originalXRange.max = null;
|
||||
} else {
|
||||
delete chart.options.scales.xAxes[0][range].max;
|
||||
}
|
||||
}
|
||||
|
||||
if (chart.crosshair.button && chart.crosshair.button.parentNode) {
|
||||
chart.crosshair.button.parentNode.removeChild(chart.crosshair.button);
|
||||
chart.crosshair.button = false;
|
||||
}
|
||||
|
||||
var syncEnabled = this.getOption(chart, 'sync', 'enabled');
|
||||
|
||||
if (!stop && update && syncEnabled) {
|
||||
|
||||
var syncGroup = this.getOption(chart, 'sync', 'group');
|
||||
|
||||
var event = new CustomEvent('reset-zoom-event');
|
||||
event.chartId = chart.id;
|
||||
event.syncGroup = syncGroup;
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
if (update) {
|
||||
var anim = chart.options.animation;
|
||||
chart.options.animation = false;
|
||||
chart.update();
|
||||
chart.options.animation = anim;
|
||||
}
|
||||
},
|
||||
|
||||
doZoom: function(chart, start, end) {
|
||||
|
||||
// swap start/end if user dragged from right to left
|
||||
if (start > end) {
|
||||
var tmp = start;
|
||||
start = end;
|
||||
end = tmp;
|
||||
}
|
||||
|
||||
// notify delegate
|
||||
var beforeZoomCallback = helpers.getValueOrDefault(chart.options.plugins.crosshair.callbacks ? chart.options.plugins.crosshair.callbacks.beforeZoom : undefined, defaultOptions.callbacks.beforeZoom);
|
||||
|
||||
if (!beforeZoomCallback(start, end)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chart.options.scales.xAxes[0].type === 'time') {
|
||||
|
||||
if (chart.options.scales.xAxes[0].time.min && chart.crosshair.originalData.length === 0) {
|
||||
chart.crosshair.originalXRange.min = chart.options.scales.xAxes[0].time.min;
|
||||
}
|
||||
if (chart.options.scales.xAxes[0].time.max && chart.crosshair.originalData.length === 0) {
|
||||
chart.crosshair.originalXRange.max = chart.options.scales.xAxes[0].time.max;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (chart.options.scales.xAxes[0].ticks.min && chart.crosshair.originalData.length === undefined) {
|
||||
chart.crosshair.originalXRange.min = chart.options.scales.xAxes[0].ticks.min;
|
||||
}
|
||||
if (chart.options.scales.xAxes[0].ticks.max && chart.crosshair.originalData.length === undefined) {
|
||||
chart.crosshair.originalXRange.max = chart.options.scales.xAxes[0].ticks.max;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// if (!chart.crosshair.button) {
|
||||
// // add restore zoom button
|
||||
// var button = document.createElement('button');
|
||||
|
||||
// var buttonText = this.getOption(chart, 'zoom', 'zoomButtonText');
|
||||
// var buttonClass = this.getOption(chart, 'zoom', 'zoomButtonClass');
|
||||
|
||||
// var buttonLabel = document.createTextNode(buttonText);
|
||||
// button.appendChild(buttonLabel);
|
||||
// button.className = buttonClass;
|
||||
// button.addEventListener('click', function() {
|
||||
// this.resetZoom(chart);
|
||||
// }.bind(this));
|
||||
// chart.canvas.parentNode.appendChild(button);
|
||||
// chart.crosshair.button = button;
|
||||
// }
|
||||
|
||||
// set axis scale
|
||||
if (chart.options.scales.xAxes[0].time) {
|
||||
chart.options.scales.xAxes[0].time.min = start;
|
||||
chart.options.scales.xAxes[0].time.max = end;
|
||||
} else {
|
||||
chart.options.scales.xAxes[0].ticks.min = start;
|
||||
chart.options.scales.xAxes[0].ticks.max = end;
|
||||
}
|
||||
|
||||
// make a copy of the original data for later restoration
|
||||
var storeOriginals = (chart.crosshair.originalData.length === 0) ? true : false;
|
||||
// filter dataset
|
||||
|
||||
for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
|
||||
|
||||
var newData = [];
|
||||
|
||||
var index = 0;
|
||||
var started = false;
|
||||
var stop = false;
|
||||
if (storeOriginals) {
|
||||
chart.crosshair.originalData[datasetIndex] = chart.data.datasets[datasetIndex].data;
|
||||
}
|
||||
|
||||
var sourceDataset = chart.crosshair.originalData[datasetIndex];
|
||||
|
||||
for (var oldDataIndex = 0; oldDataIndex < sourceDataset.length; oldDataIndex++) {
|
||||
|
||||
var oldData = sourceDataset[oldDataIndex];
|
||||
var oldDataX = this.getXScale(chart).getRightValue(oldData);
|
||||
|
||||
// append one value outside of bounds
|
||||
if (oldDataX >= start && !started && index > 0) {
|
||||
newData.push(sourceDataset[index - 1]);
|
||||
started = true;
|
||||
}
|
||||
if (oldDataX >= start && oldDataX <= end) {
|
||||
newData.push(oldData);
|
||||
}
|
||||
if (oldDataX > end && !stop && index < sourceDataset.length) {
|
||||
newData.push(oldData);
|
||||
stop = true;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
chart.data.datasets[datasetIndex].data = newData;
|
||||
}
|
||||
|
||||
chart.crosshair.start = start;
|
||||
chart.crosshair.end = end;
|
||||
|
||||
if (storeOriginals) {
|
||||
var xAxes = this.getXScale(chart);
|
||||
chart.crosshair.min = xAxes.min;
|
||||
chart.crosshair.max = xAxes.max;
|
||||
}
|
||||
|
||||
chart.update();
|
||||
|
||||
var afterZoomCallback = this.getOption(chart, 'callbacks', 'afterZoom');
|
||||
|
||||
afterZoomCallback(start, end);
|
||||
},
|
||||
|
||||
drawZoombox: function(chart) {
|
||||
|
||||
var yScale = this.getYScale(chart);
|
||||
|
||||
var borderColor = this.getOption(chart, 'zoom', 'zoomboxBorderColor');
|
||||
var fillColor = this.getOption(chart, 'zoom', 'zoomboxBackgroundColor');
|
||||
|
||||
chart.ctx.beginPath();
|
||||
chart.ctx.rect(chart.crosshair.dragStartX, yScale.getPixelForValue(yScale.max), chart.crosshair.x - chart.crosshair.dragStartX, yScale.getPixelForValue(yScale.min) - yScale.getPixelForValue(yScale.max));
|
||||
chart.ctx.lineWidth = 1;
|
||||
chart.ctx.strokeStyle = borderColor;
|
||||
chart.ctx.fillStyle = fillColor;
|
||||
chart.ctx.fill();
|
||||
chart.ctx.fillStyle = '';
|
||||
chart.ctx.stroke();
|
||||
chart.ctx.closePath();
|
||||
},
|
||||
|
||||
drawTraceLine: function(chart) {
|
||||
|
||||
var yScale = this.getYScale(chart);
|
||||
|
||||
var lineWidth = this.getOption(chart, 'line', 'width');
|
||||
var color = this.getOption(chart, 'line', 'color');
|
||||
var dashPattern = this.getOption(chart, 'line', 'dashPattern');
|
||||
var snapEnabled = this.getOption(chart, 'snap', 'enabled');
|
||||
|
||||
var lineX = chart.crosshair.x;
|
||||
var isHoverIntersectOff = chart.config.options.hover.intersect === false;
|
||||
|
||||
if (snapEnabled && isHoverIntersectOff && chart.active.length) {
|
||||
lineX = chart.active[0]._view.x;
|
||||
}
|
||||
|
||||
chart.ctx.beginPath();
|
||||
chart.ctx.setLineDash(dashPattern);
|
||||
chart.ctx.moveTo(lineX, yScale.getPixelForValue(yScale.max));
|
||||
chart.ctx.lineWidth = lineWidth;
|
||||
chart.ctx.strokeStyle = color;
|
||||
chart.ctx.lineTo(lineX, yScale.getPixelForValue(yScale.min));
|
||||
chart.ctx.stroke();
|
||||
chart.ctx.setLineDash([]);
|
||||
|
||||
},
|
||||
|
||||
drawTracePoints: function(chart) {
|
||||
|
||||
for (var chartIndex = 0; chartIndex < chart.data.datasets.length; chartIndex++) {
|
||||
|
||||
var dataset = chart.data.datasets[chartIndex];
|
||||
var meta = chart.getDatasetMeta(chartIndex);
|
||||
|
||||
var yScale = chart.scales[meta.yAxisID];
|
||||
|
||||
if (meta.hidden || !dataset.interpolate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chart.ctx.beginPath();
|
||||
chart.ctx.arc(chart.crosshair.x, yScale.getPixelForValue(dataset.interpolatedValue), 3, 0, 2 * Math.PI, false);
|
||||
chart.ctx.fillStyle = 'white';
|
||||
chart.ctx.lineWidth = 2;
|
||||
chart.ctx.strokeStyle = dataset.borderColor;
|
||||
chart.ctx.fill();
|
||||
chart.ctx.stroke();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
interpolateValues: function(chart) {
|
||||
|
||||
for (var chartIndex = 0; chartIndex < chart.data.datasets.length; chartIndex++) {
|
||||
|
||||
var dataset = chart.data.datasets[chartIndex];
|
||||
|
||||
var meta = chart.getDatasetMeta(chartIndex);
|
||||
|
||||
var xScale = chart.scales[meta.xAxisID];
|
||||
var xValue = xScale.getValueForPixel(chart.crosshair.x);
|
||||
|
||||
if (meta.hidden || !dataset.interpolate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var data = dataset.data;
|
||||
var index = data.findIndex(function(o) {
|
||||
return o.x >= xValue;
|
||||
});
|
||||
var prev = data[index - 1];
|
||||
var next = data[index];
|
||||
|
||||
if (chart.data.datasets[chartIndex].steppedLine && prev) {
|
||||
dataset.interpolatedValue = prev.y;
|
||||
} else if (prev && next) {
|
||||
var slope = (next.y - prev.y) / (next.x - prev.x);
|
||||
dataset.interpolatedValue = prev.y + (xValue - prev.x) * slope;
|
||||
} else {
|
||||
dataset.interpolatedValue = NaN;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Chart$$1.plugins.register(crosshairPlugin);
|
||||
};
|
||||
|
||||
Interpolate(Chart);
|
||||
TracePlugin(Chart);
|
||||
|
||||
})));
|
File diff suppressed because it is too large
Load Diff
|
@ -68,9 +68,13 @@
|
|||
<file>StatusQ/Components/WebEngineLoader.qml</file>
|
||||
<file>StatusQ/Components/private/StatusComboboxBackground.qml</file>
|
||||
<file>StatusQ/Components/private/StatusComboboxIndicator.qml</file>
|
||||
<file>StatusQ/Components/private/chart/Chart.js</file>
|
||||
<file>StatusQ/Components/private/chart/Chart.qml</file>
|
||||
<file>StatusQ/Components/private/chart/LICENSE</file>
|
||||
<file>StatusQ/Components/private/chart/ChartCanvas.qml</file>
|
||||
<file>StatusQ/Components/private/chart/Library/Animator.qml</file>
|
||||
<file>StatusQ/Components/private/chart/Library/Chart.bundle.js</file>
|
||||
<file>StatusQ/Components/private/chart/Library/chartjs-plugin-crosshair.js</file>
|
||||
<file>StatusQ/Components/private/chart/Library/chartjs-plugin-datalabels.js</file>
|
||||
<file>StatusQ/Components/private/chart/Library/Library.js</file>
|
||||
<file>StatusQ/Components/private/chart/Library/Polyfills.js</file>
|
||||
<file>StatusQ/Components/private/qmldir</file>
|
||||
<file>StatusQ/Components/private/qwebchannel/helpers.js</file>
|
||||
<file>StatusQ/Components/private/qwebchannel/qwebchannel.js</file>
|
||||
|
|
|
@ -30,7 +30,7 @@ StatusChartPanel {
|
|||
|
||||
onVisibleChanged: if(visible) d.resetWithSpamProtection()
|
||||
onTimeRangeTabBarIndexChanged: reset()
|
||||
onModelChanged: chart.updateToNewData()
|
||||
onModelChanged: chart.refresh()
|
||||
onCollectCommunityMetricsMessagesCount: d.lastRequestModelMetadata = d.selectedTabInfo.modelItems
|
||||
|
||||
QtObject {
|
||||
|
@ -41,6 +41,7 @@ StatusChartPanel {
|
|||
readonly property string twentyPercentBaseColor1: Theme.palette.alphaColor(baseColor1, 0.2)
|
||||
readonly property string barColor: Theme.palette.primaryColor2
|
||||
readonly property string barBorderColor: Theme.palette.primaryColor1
|
||||
|
||||
readonly property string messagesLabel: qsTr("Messages")
|
||||
|
||||
property int hoveredBarIndex: 0
|
||||
|
@ -266,17 +267,15 @@ StatusChartPanel {
|
|||
graphsModel: d.graphTabsModel
|
||||
timeRangeModel: d.modelMetadata
|
||||
onHeaderTabClicked: {
|
||||
root.chart.animateToNewData();
|
||||
chart.refresh()
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Chartjs configuration //
|
||||
/////////////////////////////
|
||||
chart.chartType: 'bar'
|
||||
chart.chartData: {
|
||||
return {
|
||||
labels: d.labels,
|
||||
datasets: [{
|
||||
chart.type: 'bar'
|
||||
chart.labels: d.labels
|
||||
chart.datasets: [{
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: d.barColor,
|
||||
|
@ -286,31 +285,27 @@ StatusChartPanel {
|
|||
hoverBorderWidth: 2,
|
||||
data: d.chartData
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
chart.chartOptions: {
|
||||
chart.options: {
|
||||
return {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
// Popup follows the cursor
|
||||
onHover: function(arg1, hoveredItems, event) {
|
||||
if(!event || hoveredItems.length == 0) {
|
||||
onHover: function(arg1, hoveredItems) {
|
||||
if(!arg1 || hoveredItems.length == 0) {
|
||||
toolTip.close()
|
||||
return
|
||||
}
|
||||
|
||||
arg1.target = chart
|
||||
d.hoveredBarIndex = hoveredItems[0]._index
|
||||
d.hoveredBarValue = hoveredItems[0]._chart.config.data.datasets[0].data[hoveredItems[0]._index]
|
||||
const position = d.getAdjustedTooltipPosition(event)
|
||||
const position = d.getAdjustedTooltipPosition(arg1)
|
||||
toolTip.popup(position.x, position.y)
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false,
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
id: 'x-axis-1',
|
||||
|
|
|
@ -183,24 +183,22 @@ Item {
|
|||
LocaleUtils.getDayMonth(value) :
|
||||
LocaleUtils.getMonthYear(value)
|
||||
}
|
||||
chart.chartType: 'line'
|
||||
chart.chartData: {
|
||||
return {
|
||||
labels: RootStore.marketHistoryIsLoading ? [] : graphDetail.labelsData,
|
||||
datasets: [{
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: graphDetail.selectedGraphType === AssetsDetailView.GraphType.Price ? 3 : 2,
|
||||
pointRadius: 0,
|
||||
data: RootStore.marketHistoryIsLoading ? [] : graphDetail.dataRange,
|
||||
parsing: false,
|
||||
}]
|
||||
}
|
||||
chart.type: 'line'
|
||||
chart.labels: RootStore.marketHistoryIsLoading ? [] : graphDetail.labelsData
|
||||
chart.datasets: {
|
||||
return [{
|
||||
xAxisId: 'x-axis-1',
|
||||
yAxisId: 'y-axis-1',
|
||||
backgroundColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 0.2)' : 'rgba(67, 96, 223, 0.2)',
|
||||
borderColor: (Theme.palette.name === "dark") ? 'rgba(136, 176, 255, 1)' : 'rgba(67, 96, 223, 1)',
|
||||
borderWidth: graphDetail.selectedGraphType === AssetsDetailView.GraphType.Price ? 3 : 2,
|
||||
pointRadius: 0,
|
||||
data: RootStore.marketHistoryIsLoading ? [] : graphDetail.dataRange,
|
||||
parsing: false,
|
||||
}]
|
||||
}
|
||||
|
||||
chart.chartOptions: {
|
||||
chart.options: {
|
||||
return {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
|
|
Loading…
Reference in New Issue