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,35 +74,12 @@ 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: {
|
||||
readonly property var lineConfig: {
|
||||
return {
|
||||
type: 'line',
|
||||
labels: d.timeRange[graphDetail.timeRangeTabBarIndex][graphDetail.selectedTimeRange],
|
||||
datasets: [{
|
||||
label: 'Price',
|
||||
|
@ -118,26 +89,14 @@ SplitView {
|
|||
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 {
|
||||
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,
|
||||
|
@ -197,6 +157,273 @@ SplitView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: [{
|
||||
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,
|
||||
//data: d.generateData()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
195
ui/StatusQ/src/StatusQ/Components/private/chart/Chart.js → ui/StatusQ/src/StatusQ/Components/private/chart/Library/Chart.bundle.js
Normal file → Executable file
195
ui/StatusQ/src/StatusQ/Components/private/chart/Chart.js → ui/StatusQ/src/StatusQ/Components/private/chart/Library/Chart.bundle.js
Normal file → Executable file
|
@ -1,32 +1,14 @@
|
|||
/*!
|
||||
* Chart.js v2.9.3
|
||||
* Chart.js v2.9.4
|
||||
* https://www.chartjs.org
|
||||
* (c) 2019 Chart.js Contributors
|
||||
* (c) 2020 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* adaptions by Elypson (Michael A. Voelkel) to get it working for QML:
|
||||
* 1) changed overall library structure with UMD and global function build
|
||||
* 2) animations are triggered via QML animator
|
||||
* 3) tooltips do not use DOM events but QML events that are injected via bindEvents now
|
||||
* 4) many smaller modifications because Chart.js relied on and used the DOM that is not available in QML in the same way
|
||||
* 5) some fixes that occured in QML like setting dashed line to solid
|
||||
* 6) personal customization, where we assumed that it leads to better looks
|
||||
*
|
||||
* also note that modifications are inspired by Shuirna (github.com/shuirna) and their changes were inspired by Julien Wintz
|
||||
*
|
||||
* (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
|
||||
* those customizations are also licensed under MIT for all matters and purposes
|
||||
*/
|
||||
|
||||
function UMD(global, factory) {
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = global || self, global.Chart = factory());
|
||||
};
|
||||
|
||||
function Chart (item, config) { 'use strict';
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||
|
||||
|
@ -2125,6 +2107,10 @@ if (typeof window !== 'undefined') {
|
|||
|
||||
var chartjsColor = Color;
|
||||
|
||||
function isValidKey(key) {
|
||||
return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @namespace Chart.helpers
|
||||
*/
|
||||
|
@ -2300,7 +2286,7 @@ var helpers = {
|
|||
}
|
||||
|
||||
if (helpers.isObject(source)) {
|
||||
var target = {};
|
||||
var target = Object.create(source);
|
||||
var keys = Object.keys(source);
|
||||
var klen = keys.length;
|
||||
var k = 0;
|
||||
|
@ -2321,6 +2307,12 @@ var helpers = {
|
|||
* @private
|
||||
*/
|
||||
_merger: function(key, target, source, options) {
|
||||
if (!isValidKey(key)) {
|
||||
// We want to ensure we do not copy prototypes over
|
||||
// as this can pollute global namespaces
|
||||
return;
|
||||
}
|
||||
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
|
@ -2336,6 +2328,12 @@ var helpers = {
|
|||
* @private
|
||||
*/
|
||||
_mergerIf: function(key, target, source) {
|
||||
if (!isValidKey(key)) {
|
||||
// We want to ensure we do not copy prototypes over
|
||||
// as this can pollute global namespaces
|
||||
return;
|
||||
}
|
||||
|
||||
var tval = target[key];
|
||||
var sval = source[key];
|
||||
|
||||
|
@ -3504,13 +3502,10 @@ var core_animations = {
|
|||
requestAnimationFrame: function() {
|
||||
var me = this;
|
||||
if (me.request === null) {
|
||||
return;
|
||||
// TBD: animation should work somehow; what is startDigest?
|
||||
|
||||
// Skip animation frame requests until the active one is executed.
|
||||
// This can happen when processing mouse events, e.g. 'mousemove'
|
||||
// and 'mouseout' events will trigger multiple renders.
|
||||
me.request = helpers$1.requestAnimFrame.call(undefined, function() {
|
||||
me.request = helpers$1.requestAnimFrame.call(window, function() {
|
||||
me.request = null;
|
||||
me.startDigest();
|
||||
});
|
||||
|
@ -3837,7 +3832,7 @@ helpers$1.extend(DatasetController.prototype, {
|
|||
*/
|
||||
_configure: function() {
|
||||
var me = this;
|
||||
me._config = helpers$1.merge({}, [
|
||||
me._config = helpers$1.merge(Object.create(null), [
|
||||
me.chart.options.datasets[me._type],
|
||||
me.getDataset(),
|
||||
], {
|
||||
|
@ -4405,11 +4400,6 @@ var element_line = core_element.extend({
|
|||
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
|
||||
}
|
||||
|
||||
// line dash fix for QML
|
||||
if(ctx.getLineDash && ctx.getLineDash().length === 0) {
|
||||
ctx.setLineDash([99999]);
|
||||
}
|
||||
|
||||
ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);
|
||||
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
|
||||
ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);
|
||||
|
@ -4679,6 +4669,7 @@ var element_rectangle = core_element.extend({
|
|||
if (outer.w !== inner.w || outer.h !== inner.h) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = vm.borderColor;
|
||||
ctx.lineWidth = vm.borderWidth;
|
||||
ctx.strokeRect(inner.x, inner.y, inner.w, inner.h);
|
||||
ctx.fill('evenodd');
|
||||
}
|
||||
|
@ -6147,9 +6138,9 @@ var controller_line = core_datasetController.extend({
|
|||
};
|
||||
|
||||
model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||||
model.borderColor = "rgba(255,0,0,1.)";//valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = 1;//valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = 2;//valueOrDefault$6(options.hoverRadius, options.radius);
|
||||
model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||||
model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
|
||||
model.radius = valueOrDefault$6(options.hoverRadius, options.radius);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -7110,7 +7101,8 @@ function updateDims(chartArea, params, layout) {
|
|||
chartArea.h = newHeight;
|
||||
|
||||
// return true if chart area changed in layout's direction
|
||||
return layout.horizontal ? newWidth !== chartArea.w : newHeight !== chartArea.h;
|
||||
var sizes = layout.horizontal ? [newWidth, chartArea.w] : [newHeight, chartArea.h];
|
||||
return sizes[0] !== sizes[1] && (!isNaN(sizes[0]) || !isNaN(sizes[1]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7414,7 +7406,7 @@ var platform_basic = {
|
|||
}
|
||||
};
|
||||
|
||||
var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n";
|
||||
var platform_dom = "/*\r\n * DOM element rendering detection\r\n * https://davidwalsh.name/detect-node-insertion\r\n */\r\n@keyframes chartjs-render-animation {\r\n\tfrom { opacity: 0.99; }\r\n\tto { opacity: 1; }\r\n}\r\n\r\n.chartjs-render-monitor {\r\n\tanimation: chartjs-render-animation 0.001s;\r\n}\r\n\r\n/*\r\n * DOM element resizing detection\r\n * https://github.com/marcj/css-element-queries\r\n */\r\n.chartjs-size-monitor,\r\n.chartjs-size-monitor-expand,\r\n.chartjs-size-monitor-shrink {\r\n\tposition: absolute;\r\n\tdirection: ltr;\r\n\tleft: 0;\r\n\ttop: 0;\r\n\tright: 0;\r\n\tbottom: 0;\r\n\toverflow: hidden;\r\n\tpointer-events: none;\r\n\tvisibility: hidden;\r\n\tz-index: -1;\r\n}\r\n\r\n.chartjs-size-monitor-expand > div {\r\n\tposition: absolute;\r\n\twidth: 1000000px;\r\n\theight: 1000000px;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n\r\n.chartjs-size-monitor-shrink > div {\r\n\tposition: absolute;\r\n\twidth: 200%;\r\n\theight: 200%;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n";
|
||||
|
||||
var platform_dom$1 = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
|
@ -8122,7 +8114,7 @@ var core_scaleService = {
|
|||
},
|
||||
getScaleDefaults: function(type) {
|
||||
// Return the scale defaults merged with the global settings so that we always use the latest ones
|
||||
return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {};
|
||||
return this.defaults.hasOwnProperty(type) ? helpers$1.merge(Object.create(null), [core_defaults.scale, this.defaults[type]]) : {};
|
||||
},
|
||||
updateScaleDefaults: function(type, additions) {
|
||||
var me = this;
|
||||
|
@ -9197,7 +9189,7 @@ core_defaults._set('global', {
|
|||
* returns a deep copy of the result, thus doesn't alter inputs.
|
||||
*/
|
||||
function mergeScaleConfig(/* config objects ... */) {
|
||||
return helpers$1.merge({}, [].slice.call(arguments), {
|
||||
return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
|
||||
merger: function(key, target, source, options) {
|
||||
if (key === 'xAxes' || key === 'yAxes') {
|
||||
var slen = source[key].length;
|
||||
|
@ -9237,9 +9229,9 @@ function mergeScaleConfig(/* config objects ... */) {
|
|||
* a deep copy of the result, thus doesn't alter inputs.
|
||||
*/
|
||||
function mergeConfig(/* config objects ... */) {
|
||||
return helpers$1.merge({}, [].slice.call(arguments), {
|
||||
return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
|
||||
merger: function(key, target, source, options) {
|
||||
var tval = target[key] || {};
|
||||
var tval = target[key] || Object.create(null);
|
||||
var sval = source[key];
|
||||
|
||||
if (key === 'scales') {
|
||||
|
@ -9256,7 +9248,7 @@ function mergeConfig(/* config objects ... */) {
|
|||
}
|
||||
|
||||
function initConfig(config) {
|
||||
config = config || {};
|
||||
config = config || Object.create(null);
|
||||
|
||||
// Do NOT use mergeConfig for the data object because this method merges arrays
|
||||
// and so would change references to labels and datasets, preventing data updates.
|
||||
|
@ -9441,6 +9433,8 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
|
||||
canvas.width = me.width = newWidth;
|
||||
canvas.height = me.height = newHeight;
|
||||
canvas.style.width = newWidth + 'px';
|
||||
canvas.style.height = newHeight + 'px';
|
||||
|
||||
helpers$1.retinaScale(me, options.devicePixelRatio);
|
||||
|
||||
|
@ -9783,7 +9777,6 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
}
|
||||
|
||||
var animationOptions = me.options.animation;
|
||||
|
||||
var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration);
|
||||
var lazy = config.lazy;
|
||||
|
||||
|
@ -9920,7 +9913,6 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
}
|
||||
|
||||
metasets = me._getSortedVisibleDatasetMetas();
|
||||
|
||||
for (i = metasets.length - 1; i >= 0; --i) {
|
||||
me.drawDataset(metasets[i], easingValue);
|
||||
}
|
||||
|
@ -9991,7 +9983,6 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
getElementsAtEventForMode: function(e, mode, options) {
|
||||
var method = core_interaction.modes[mode];
|
||||
if (typeof method === 'function') {
|
||||
// TBD: method might be nearest here
|
||||
return method(this, e, options);
|
||||
}
|
||||
|
||||
|
@ -10105,7 +10096,7 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
bindEvents: function(setHandler) {
|
||||
bindEvents: function() {
|
||||
var me = this;
|
||||
var listeners = me._listeners = {};
|
||||
var listener = function() {
|
||||
|
@ -10113,12 +10104,7 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
};
|
||||
|
||||
helpers$1.each(me.options.events, function(type) {
|
||||
if(setHandler) {
|
||||
setHandler(listener);
|
||||
}
|
||||
else
|
||||
platform.addEventListener(me, type, listener);
|
||||
|
||||
listeners[type] = listener;
|
||||
});
|
||||
|
||||
|
@ -10239,7 +10225,7 @@ helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
|||
|
||||
// Invoke onHover hook
|
||||
// Need to call with native event here to not break backwards compatibility
|
||||
helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active, e], me);
|
||||
helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
|
||||
|
||||
if (e.type === 'mouseup' || e.type === 'click') {
|
||||
if (options.onClick) {
|
||||
|
@ -10665,9 +10651,9 @@ var core_helpers = function() {
|
|||
// -- DOM methods
|
||||
helpers$1.getRelativePosition = function(evt, chart) {
|
||||
var mouseX, mouseY;
|
||||
var e = evt;//evt.originalEvent || evt;
|
||||
var e = evt.originalEvent || evt;
|
||||
var canvas = evt.target || evt.srcElement;
|
||||
var boundingRect = {left: 0, top: 0, right: canvas.width, bottom: canvas.height};//canvas.getBoundingClientRect();
|
||||
var boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
var touches = e.touches;
|
||||
if (touches && touches.length > 0) {
|
||||
|
@ -10682,12 +10668,10 @@ var core_helpers = function() {
|
|||
// Scale mouse coordinates into canvas coordinates
|
||||
// by following the pattern laid out by 'jerryj' in the comments of
|
||||
// https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
|
||||
|
||||
// Elypson: remove paddings left/right
|
||||
var paddingLeft = parseFloat(0);//helpers$1.getStyle(canvas, 'padding-left'));
|
||||
var paddingTop = parseFloat(0);//helpers$1.getStyle(canvas, 'padding-top'));
|
||||
var paddingRight = parseFloat(0);//helpers$1.getStyle(canvas, 'padding-right'));
|
||||
var paddingBottom = parseFloat(0);//helpers$1.getStyle(canvas, 'padding-bottom'));
|
||||
var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left'));
|
||||
var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top'));
|
||||
var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right'));
|
||||
var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom'));
|
||||
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
|
||||
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
|
||||
|
||||
|
@ -10779,10 +10763,32 @@ var core_helpers = function() {
|
|||
return parent;
|
||||
};
|
||||
helpers$1.getMaximumWidth = function(domNode) {
|
||||
return domNode.width;
|
||||
var container = helpers$1._getParentNode(domNode);
|
||||
if (!container) {
|
||||
return domNode.clientWidth;
|
||||
}
|
||||
|
||||
var clientWidth = container.clientWidth;
|
||||
var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth);
|
||||
var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth);
|
||||
|
||||
var w = clientWidth - paddingLeft - paddingRight;
|
||||
var cw = helpers$1.getConstraintWidth(domNode);
|
||||
return isNaN(cw) ? w : Math.min(w, cw);
|
||||
};
|
||||
helpers$1.getMaximumHeight = function(domNode) {
|
||||
return domNode.height;
|
||||
var container = helpers$1._getParentNode(domNode);
|
||||
if (!container) {
|
||||
return domNode.clientHeight;
|
||||
}
|
||||
|
||||
var clientHeight = container.clientHeight;
|
||||
var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight);
|
||||
var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight);
|
||||
|
||||
var h = clientHeight - paddingTop - paddingBottom;
|
||||
var ch = helpers$1.getConstraintHeight(domNode);
|
||||
return isNaN(ch) ? h : Math.min(h, ch);
|
||||
};
|
||||
helpers$1.getStyle = function(el, property) {
|
||||
return el.currentStyle ?
|
||||
|
@ -10900,7 +10906,10 @@ var core_helpers = function() {
|
|||
};
|
||||
|
||||
helpers$1.getHoverColor = function(colorValue) {
|
||||
return Color(colorValue).saturate(0.5).darken(0.1).rgbString();
|
||||
/* global CanvasPattern */
|
||||
return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ?
|
||||
colorValue :
|
||||
helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString();
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -11220,6 +11229,8 @@ function computeLabelSizes(ctx, tickFonts, ticks, caches) {
|
|||
var widths = [];
|
||||
var heights = [];
|
||||
var offsets = [];
|
||||
var widestLabelSize = 0;
|
||||
var highestLabelSize = 0;
|
||||
var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
|
@ -11247,11 +11258,13 @@ function computeLabelSizes(ctx, tickFonts, ticks, caches) {
|
|||
widths.push(width);
|
||||
heights.push(height);
|
||||
offsets.push(lineHeight / 2);
|
||||
widestLabelSize = Math.max(width, widestLabelSize);
|
||||
highestLabelSize = Math.max(height, highestLabelSize);
|
||||
}
|
||||
garbageCollect(caches, length);
|
||||
|
||||
widest = widths.indexOf(Math.max.apply(null, widths));
|
||||
highest = heights.indexOf(Math.max.apply(null, heights));
|
||||
widest = widths.indexOf(widestLabelSize);
|
||||
highest = heights.indexOf(highestLabelSize);
|
||||
|
||||
function valueAt(idx) {
|
||||
return {
|
||||
|
@ -11569,6 +11582,7 @@ var Scale = core_element.extend({
|
|||
// Generate labels using all non-skipped ticks
|
||||
labels = me._convertTicksToLabels(me._ticksToDraw);
|
||||
}
|
||||
|
||||
me.ticks = labels; // BACKWARD COMPATIBILITY
|
||||
|
||||
// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
|
||||
|
@ -12336,11 +12350,6 @@ var Scale = core_element.extend({
|
|||
ctx.lineDashOffset = item.borderDashOffset;
|
||||
}
|
||||
|
||||
// MV workaround, as it seems that emptying line dash does not work
|
||||
if(ctx.getLineDash && ctx.getLineDash().length === 0) {
|
||||
ctx.setLineDash([9999]);
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
if (gridLines.drawTicks) {
|
||||
|
@ -13051,12 +13060,7 @@ var scale_linear = scale_linearbase.extend({
|
|||
},
|
||||
|
||||
getLabelForIndex: function(index, datasetIndex) {
|
||||
var scaleLabel = this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
|
||||
var optionsTooltips = this.chart.options.tooltips;
|
||||
if (optionsTooltips && optionsTooltips.format && optionsTooltips.format.enabled && optionsTooltips.format.valueCallback) {
|
||||
return optionsTooltips.format.valueCallback(scaleLabel);
|
||||
}
|
||||
return scaleLabel;
|
||||
return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
|
||||
},
|
||||
|
||||
// Utils
|
||||
|
@ -13647,11 +13651,6 @@ function drawRadiusLine(scale, gridLineOpts, radius, index) {
|
|||
ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
|
||||
}
|
||||
|
||||
// line dash fix for QML
|
||||
if(ctx.getLineDash && ctx.getLineDash().length === 0) {
|
||||
ctx.setLineDash([99999]);
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
if (circular) {
|
||||
// Draw circular arcs between the points
|
||||
|
@ -13869,11 +13868,6 @@ var scale_radialLinear = scale_linearbase.extend({
|
|||
ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
|
||||
}
|
||||
|
||||
// line dash fix for QML
|
||||
if(ctx.getLineDash && ctx.getLineDash().length === 0) {
|
||||
ctx.setLineDash([99999]);
|
||||
}
|
||||
|
||||
for (i = me.chart.data.labels.length - 1; i >= 0; i--) {
|
||||
offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
|
||||
position = me.getPointPosition(i, offset);
|
||||
|
@ -14570,11 +14564,6 @@ var scale_time = core_scale.extend({
|
|||
if (typeof label === 'string') {
|
||||
return label;
|
||||
}
|
||||
var tooltipsFormat = me.chart.options.tooltips.format;
|
||||
if (tooltipsFormat && tooltipsFormat.enabled && tooltipsFormat.callback) {
|
||||
return tooltipsFormat.callback(label)
|
||||
}
|
||||
|
||||
return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime);
|
||||
},
|
||||
|
||||
|
@ -14593,13 +14582,7 @@ var scale_time = core_scale.extend({
|
|||
var tick = ticks[index];
|
||||
var tickOpts = options.ticks;
|
||||
var major = majorUnit && majorFormat && tick && tick.major;
|
||||
var labelFormat = me.chart.options.scales.labelFormat;
|
||||
var label;
|
||||
if (labelFormat && labelFormat.enabled && labelFormat.callback)
|
||||
label = labelFormat.callback(time);
|
||||
else
|
||||
label = adapter.format(time, format ? format : major ? majorFormat : minorFormat);
|
||||
|
||||
var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat);
|
||||
var nestedTickOpts = major ? tickOpts.major : tickOpts.minor;
|
||||
var formatter = resolve$5([
|
||||
nestedTickOpts.callback,
|
||||
|
@ -20152,10 +20135,6 @@ var Legend = core_element.extend({
|
|||
ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash));
|
||||
}
|
||||
|
||||
if(ctx.getLineDash && ctx.getLineDash().length === 0) {
|
||||
ctx.setLineDash([99999]); // fix for QML because it does not make lines solid once being unsolid once
|
||||
}
|
||||
|
||||
if (labelOpts && labelOpts.usePointStyle) {
|
||||
// Recalculate x and y for drawPoint() because its expecting
|
||||
// x and y to be center of figure (instead of top left)
|
||||
|
@ -20687,7 +20666,6 @@ for (var k in plugins) {
|
|||
core_controller.platform.initialize();
|
||||
|
||||
var src = core_controller;
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.Chart = core_controller;
|
||||
}
|
||||
|
@ -20792,11 +20770,6 @@ core_controller.helpers.each(
|
|||
}
|
||||
);
|
||||
|
||||
return new src(item, config);
|
||||
}
|
||||
return src;
|
||||
|
||||
function build(item, config) {
|
||||
//UMD(me, meChart);
|
||||
//var y = new test();
|
||||
return new Chart(item, config);
|
||||
}
|
||||
})));
|
|
@ -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,11 +183,10 @@ Item {
|
|||
LocaleUtils.getDayMonth(value) :
|
||||
LocaleUtils.getMonthYear(value)
|
||||
}
|
||||
chart.chartType: 'line'
|
||||
chart.chartData: {
|
||||
return {
|
||||
labels: RootStore.marketHistoryIsLoading ? [] : graphDetail.labelsData,
|
||||
datasets: [{
|
||||
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)',
|
||||
|
@ -198,9 +197,8 @@ Item {
|
|||
parsing: false,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
chart.chartOptions: {
|
||||
chart.options: {
|
||||
return {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
|
|
Loading…
Reference in New Issue