mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Graph NG: annotations display (#27972)
* Annotations support POC * Fix markers memoization * dev dashboard update * Update public/app/plugins/panel/graph3/plugins/AnnotationsPlugin.tsx
This commit is contained in:
parent
a2816ee64a
commit
aa6c98f7ff
@ -22,6 +22,9 @@
|
|||||||
"datasource": null,
|
"datasource": null,
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -36,11 +39,11 @@
|
|||||||
"alpha": 0.1
|
"alpha": 0.1
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -355,6 +358,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -371,10 +377,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
"show": true,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -560,6 +563,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -576,10 +582,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
"show": true,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -678,6 +681,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -694,10 +700,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
"show": true,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -811,6 +814,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -827,10 +833,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": false,
|
"show": false,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -899,7 +902,7 @@
|
|||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "custom.line.color",
|
"id": "color",
|
||||||
"value": {
|
"value": {
|
||||||
"fixedColor": "purple",
|
"fixedColor": "purple",
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
@ -960,6 +963,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -976,10 +982,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": false,
|
"show": false,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1048,7 +1051,7 @@
|
|||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "custom.line.color",
|
"id": "color",
|
||||||
"value": {
|
"value": {
|
||||||
"fixedColor": "purple",
|
"fixedColor": "purple",
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
@ -1109,6 +1112,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -1125,10 +1131,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": false,
|
"show": false,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1197,7 +1200,7 @@
|
|||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "custome.line.color",
|
"id": "color",
|
||||||
"value": {
|
"value": {
|
||||||
"fixedColor": "purple",
|
"fixedColor": "purple",
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
@ -1258,6 +1261,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -1274,10 +1280,7 @@
|
|||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": false,
|
"show": false,
|
||||||
"width": 1,
|
"width": 1
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1346,7 +1349,7 @@
|
|||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "custom.line.color",
|
"id": "color",
|
||||||
"value": {
|
"value": {
|
||||||
"fixedColor": "purple",
|
"fixedColor": "purple",
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
@ -1422,6 +1425,9 @@
|
|||||||
"datasource": null,
|
"datasource": null,
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -1437,11 +1443,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1506,6 +1512,9 @@
|
|||||||
"datasource": null,
|
"datasource": null,
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -1521,11 +1530,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1590,6 +1599,9 @@
|
|||||||
"datasource": null,
|
"datasource": null,
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -1604,11 +1616,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1668,6 +1680,96 @@
|
|||||||
"timeShift": null,
|
"timeShift": null,
|
||||||
"title": "No tooltip",
|
"title": "No tooltip",
|
||||||
"type": "graph3"
|
"type": "graph3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "gdev-testdata",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axis": {
|
||||||
|
"grid": true,
|
||||||
|
"label": "Temperature",
|
||||||
|
"side": 3,
|
||||||
|
"width": 60
|
||||||
|
},
|
||||||
|
"bars": {
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"fill": {
|
||||||
|
"alpha": 0
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
|
},
|
||||||
|
"nullValues": "null",
|
||||||
|
"points": {
|
||||||
|
"radius": 8,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "celsius"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 22
|
||||||
|
},
|
||||||
|
"id": 9,
|
||||||
|
"options": {
|
||||||
|
"graph": {
|
||||||
|
"realTimeUpdates": false
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"asTable": false,
|
||||||
|
"isVisible": true,
|
||||||
|
"placement": "under"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "multi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.2.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "C",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "1,20,90,30,5,0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "10,25,40"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Diff number of datapoints milt multi series tooltip",
|
||||||
|
"transformations": [],
|
||||||
|
"type": "graph3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Tooltip",
|
"title": "Tooltip",
|
||||||
@ -1688,6 +1790,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -1702,11 +1807,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1775,6 +1880,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -1795,10 +1903,7 @@
|
|||||||
"show": false
|
"show": false
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
"show": true
|
||||||
"color": {
|
|
||||||
"mode": "fixed"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"points": {
|
"points": {
|
||||||
"radius": 8,
|
"radius": 8,
|
||||||
@ -1809,6 +1914,9 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"show": true,
|
"show": true,
|
||||||
"width": 1
|
"width": 1
|
||||||
},
|
},
|
||||||
@ -1879,6 +1987,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -1893,11 +2004,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "connected",
|
"nullValues": "connected",
|
||||||
"points": {
|
"points": {
|
||||||
@ -1981,6 +2092,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"axis": {
|
"axis": {
|
||||||
"grid": true,
|
"grid": true,
|
||||||
@ -1995,11 +2109,11 @@
|
|||||||
"alpha": 0
|
"alpha": 0
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": true,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -2031,7 +2145,7 @@
|
|||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 16
|
"y": 16
|
||||||
},
|
},
|
||||||
"id": 9,
|
"id": 43,
|
||||||
"options": {
|
"options": {
|
||||||
"graph": {
|
"graph": {
|
||||||
"realTimeUpdates": false
|
"realTimeUpdates": false
|
||||||
@ -2083,6 +2197,9 @@
|
|||||||
"datasource": "gdev-testdata",
|
"datasource": "gdev-testdata",
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"align": null,
|
"align": null,
|
||||||
"axis": {
|
"axis": {
|
||||||
@ -2098,11 +2215,11 @@
|
|||||||
"alpha": 0.2
|
"alpha": 0.2
|
||||||
},
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"show": false,
|
|
||||||
"width": 1,
|
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
}
|
},
|
||||||
|
"show": false,
|
||||||
|
"width": 1
|
||||||
},
|
},
|
||||||
"nullValues": "null",
|
"nullValues": "null",
|
||||||
"points": {
|
"points": {
|
||||||
@ -2175,7 +2292,7 @@
|
|||||||
"value": false
|
"value": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "custom.line.color",
|
"id": "color",
|
||||||
"value": {
|
"value": {
|
||||||
"fixedColor": "purple",
|
"fixedColor": "purple",
|
||||||
"mode": "fixed"
|
"mode": "fixed"
|
||||||
@ -2229,6 +2346,124 @@
|
|||||||
],
|
],
|
||||||
"title": "Streaming",
|
"title": "Streaming",
|
||||||
"type": "row"
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": true,
|
||||||
|
"datasource": null,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 17
|
||||||
|
},
|
||||||
|
"id": 45,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axis": {
|
||||||
|
"grid": true,
|
||||||
|
"label": "",
|
||||||
|
"side": 3,
|
||||||
|
"width": 60
|
||||||
|
},
|
||||||
|
"bars": {
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"fill": {
|
||||||
|
"alpha": 0.1
|
||||||
|
},
|
||||||
|
"line": {
|
||||||
|
"color": {
|
||||||
|
"mode": "fixed"
|
||||||
|
},
|
||||||
|
"show": true,
|
||||||
|
"width": 1
|
||||||
|
},
|
||||||
|
"nullValues": "null",
|
||||||
|
"points": {
|
||||||
|
"radius": 4,
|
||||||
|
"show": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 11,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 18
|
||||||
|
},
|
||||||
|
"id": 46,
|
||||||
|
"options": {
|
||||||
|
"graph": {
|
||||||
|
"realTimeUpdates": false
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"asTable": false,
|
||||||
|
"isVisible": true,
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.3.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "C",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "D",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "E",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "F",
|
||||||
|
"scenarioId": "annotations",
|
||||||
|
"stringInput": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "React NG graph",
|
||||||
|
"type": "graph3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Annotations",
|
||||||
|
"type": "row"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"refresh": false,
|
"refresh": false,
|
||||||
@ -2239,15 +2474,14 @@
|
|||||||
"list": []
|
"list": []
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"from": "now-5m",
|
"from": "now-2d",
|
||||||
"to": "now"
|
"to": "now"
|
||||||
},
|
},
|
||||||
"timepicker": {
|
"timepicker": {
|
||||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||||
},
|
},
|
||||||
"timezone": "Africa/Accra",
|
"timezone": "Africa/Accra",
|
||||||
"title": "Panel Tests - Graph NG",
|
"title": "Graph NG - tests",
|
||||||
"uid": "TkZXxlNG3",
|
"uid": "TkZXxlNG3",
|
||||||
"version": 61,
|
"version": 65
|
||||||
"tags": ["gdev", "panel-tests", "graph"]
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useLayoutEffect, useRef } from 'react';
|
import React, { useState, useLayoutEffect, useRef, HTMLAttributes } from 'react';
|
||||||
import { stylesFactory } from '../../themes/stylesFactory';
|
import { stylesFactory } from '../../themes/stylesFactory';
|
||||||
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
||||||
import { css } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { useTheme } from '../../themes/ThemeContext';
|
import { useTheme } from '../../themes/ThemeContext';
|
||||||
import useWindowSize from 'react-use/lib/useWindowSize';
|
import useWindowSize from 'react-use/lib/useWindowSize';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
|
|
||||||
interface TooltipContainerProps {
|
interface TooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
offset: { x: number; y: number };
|
offset: { x: number; y: number };
|
||||||
children?: JSX.Element;
|
children?: JSX.Element;
|
||||||
@ -27,7 +27,13 @@ const getTooltipContainerStyles = stylesFactory((theme: GrafanaTheme) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const TooltipContainer: React.FC<TooltipContainerProps> = ({ position, offset, children }) => {
|
export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...otherProps
|
||||||
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||||
const { width, height } = useWindowSize();
|
const { width, height } = useWindowSize();
|
||||||
@ -70,7 +76,8 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({ position, of
|
|||||||
top: 0,
|
top: 0,
|
||||||
transform: `translate3d(${placement.x}px, ${placement.y}px, 0)`,
|
transform: `translate3d(${placement.x}px, ${placement.y}px, 0)`,
|
||||||
}}
|
}}
|
||||||
className={styles.wrapper}
|
{...otherProps}
|
||||||
|
className={cx(styles.wrapper, className)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,13 @@ export const Tag = forwardRef<HTMLElement, Props>(({ name, onClick, className, c
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span key={name} ref={ref} onClick={onTagClick} className={cx(styles.wrapper, className)} {...rest}>
|
<span
|
||||||
|
key={name}
|
||||||
|
ref={ref}
|
||||||
|
onClick={onTagClick}
|
||||||
|
className={cx(styles.wrapper, className, onClick && styles.hover)}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -50,8 +56,9 @@ const getTagStyles = (theme: GrafanaTheme, name: string, colorIndex?: number) =>
|
|||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
padding: 3px 6px;
|
padding: 3px 6px;
|
||||||
border-radius: ${theme.border.radius.md};
|
border-radius: ${theme.border.radius.md};
|
||||||
|
`,
|
||||||
:hover {
|
hover: css`
|
||||||
|
&:hover {
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ export * from './uPlot/geometries';
|
|||||||
export { usePlotConfigContext } from './uPlot/context';
|
export { usePlotConfigContext } from './uPlot/context';
|
||||||
export { Canvas } from './uPlot/Canvas';
|
export { Canvas } from './uPlot/Canvas';
|
||||||
export * from './uPlot/plugins';
|
export * from './uPlot/plugins';
|
||||||
|
export { usePlotContext, usePlotData, usePlotPluginContext } from './uPlot/context';
|
||||||
|
|
||||||
export { Gauge } from './Gauge/Gauge';
|
export { Gauge } from './Gauge/Gauge';
|
||||||
export { Graph } from './Graph/Graph';
|
export { Graph } from './Graph/Graph';
|
||||||
@ -129,6 +130,7 @@ export { FadeTransition } from './transitions/FadeTransition';
|
|||||||
export { SlideOutTransition } from './transitions/SlideOutTransition';
|
export { SlideOutTransition } from './transitions/SlideOutTransition';
|
||||||
export { Segment, SegmentAsync, SegmentInput, SegmentSelect } from './Segment/';
|
export { Segment, SegmentAsync, SegmentInput, SegmentSelect } from './Segment/';
|
||||||
export { default as Chart } from './Chart';
|
export { default as Chart } from './Chart';
|
||||||
|
export { TooltipContainer } from './Chart/TooltipContainer';
|
||||||
export { Drawer } from './Drawer/Drawer';
|
export { Drawer } from './Drawer/Drawer';
|
||||||
export { Slider } from './Slider/Slider';
|
export { Slider } from './Slider/Slider';
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export const UPlotChart: React.FC<PlotProps> = props => {
|
|||||||
// Memoize plot context
|
// Memoize plot context
|
||||||
const plotCtx = useMemo(() => {
|
const plotCtx = useMemo(() => {
|
||||||
return buildPlotContext(registerPlugin, addSeries, addAxis, addScale, canvasRef, props.data, plotInstance);
|
return buildPlotContext(registerPlugin, addSeries, addAxis, addScale, canvasRef, props.data, plotInstance);
|
||||||
}, [registerPlugin, canvasRef, props.data, plotInstance, addSeries, addAxis, addScale]);
|
}, [registerPlugin, addSeries, addAxis, addScale, canvasRef, props.data, plotInstance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlotContext.Provider value={plotCtx}>
|
<PlotContext.Provider value={plotCtx}>
|
||||||
|
@ -36,11 +36,11 @@ export const usePlotPlugins = () => {
|
|||||||
(plugin: PlotPlugin) => {
|
(plugin: PlotPlugin) => {
|
||||||
pluginLog(plugin.id, false, 'register');
|
pluginLog(plugin.id, false, 'register');
|
||||||
|
|
||||||
if (plugins.hasOwnProperty(plugin.id)) {
|
|
||||||
throw new Error(`${plugin.id} that is already registered`);
|
|
||||||
}
|
|
||||||
|
|
||||||
setPlugins(plugs => {
|
setPlugins(plugs => {
|
||||||
|
if (plugs.hasOwnProperty(plugin.id)) {
|
||||||
|
throw new Error(`${plugin.id} that is already registered`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...plugs,
|
...plugs,
|
||||||
[plugin.id]: plugin,
|
[plugin.id]: plugin,
|
||||||
@ -58,7 +58,7 @@ export const usePlotPlugins = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[setPlugins, plugins]
|
[setPlugins]
|
||||||
);
|
);
|
||||||
|
|
||||||
// When uPlot mounts let's check if there are any plugins pending registration
|
// When uPlot mounts let's check if there are any plugins pending registration
|
||||||
@ -74,7 +74,7 @@ export const usePlotPlugins = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
arePluginsReady,
|
arePluginsReady,
|
||||||
plugins,
|
plugins: plugins || {},
|
||||||
registerPlugin,
|
registerPlugin,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@ interface ContextMenuPluginProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ContextMenuPlugin: React.FC<ContextMenuPluginProps> = ({ onClose }) => {
|
export const ContextMenuPlugin: React.FC<ContextMenuPluginProps> = ({ onClose }) => {
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
|
@ -32,7 +32,7 @@ export const LegendPlugin: React.FC<LegendPluginProps> = ({ placement, displayMo
|
|||||||
label: getFieldDisplayName(field, data),
|
label: getFieldDisplayName(field, data),
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
//flot vs uPlot differences
|
//flot vs uPlot differences
|
||||||
yAxis: (field.config.custom as GraphCustomFieldConfig).axis?.side === 1 ? 3 : 1,
|
yAxis: (field.config.custom as GraphCustomFieldConfig)?.axis?.side === 1 ? 3 : 1,
|
||||||
});
|
});
|
||||||
seriesIdx++;
|
seriesIdx++;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,9 @@ export const shouldReinitialisePlot = (prevConfig?: uPlot.Options, config?: uPlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!prevConfig && config) {
|
if (!prevConfig && config) {
|
||||||
|
if (config.width === 0 || config.height === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import { VizLayout } from './VizLayout';
|
|||||||
|
|
||||||
import { Axis } from '@grafana/ui/src/components/uPlot/geometries/Axis';
|
import { Axis } from '@grafana/ui/src/components/uPlot/geometries/Axis';
|
||||||
import { timeFormatToTemplate } from '@grafana/ui/src/components/uPlot/utils';
|
import { timeFormatToTemplate } from '@grafana/ui/src/components/uPlot/utils';
|
||||||
|
import { AnnotationsPlugin } from './plugins/AnnotationsPlugin';
|
||||||
|
|
||||||
interface GraphPanelProps extends PanelProps<Options> {}
|
interface GraphPanelProps extends PanelProps<Options> {}
|
||||||
|
|
||||||
@ -232,6 +233,7 @@ export const GraphPanel: React.FC<GraphPanelProps> = ({
|
|||||||
<ZoomPlugin onZoom={onChangeTimeRange} />
|
<ZoomPlugin onZoom={onChangeTimeRange} />
|
||||||
<ContextMenuPlugin />
|
<ContextMenuPlugin />
|
||||||
|
|
||||||
|
{data.annotations && <AnnotationsPlugin annotations={data.annotations} timeZone={timeZone} />}
|
||||||
{/* TODO: */}
|
{/* TODO: */}
|
||||||
{/*<AnnotationsEditorPlugin />*/}
|
{/*<AnnotationsEditorPlugin />*/}
|
||||||
</UPlotChart>
|
</UPlotChart>
|
||||||
|
145
public/app/plugins/panel/graph3/plugins/AnnotationMarker.tsx
Normal file
145
public/app/plugins/panel/graph3/plugins/AnnotationMarker.tsx
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
|
import { AnnotationEvent, GrafanaTheme } from '@grafana/data';
|
||||||
|
import { HorizontalGroup, Portal, Tag, TooltipContainer, useStyles } from '@grafana/ui';
|
||||||
|
import { css, cx } from 'emotion';
|
||||||
|
|
||||||
|
interface AnnotationMarkerProps {
|
||||||
|
formatTime: (value: number) => string;
|
||||||
|
annotationEvent: AnnotationEvent;
|
||||||
|
x: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AnnotationMarker: React.FC<AnnotationMarkerProps> = ({ annotationEvent, x, formatTime }) => {
|
||||||
|
const styles = useStyles(getAnnotationMarkerStyles);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const markerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const annotationPopoverRef = useRef<HTMLDivElement>(null);
|
||||||
|
const popoverRenderTimeout = useRef<NodeJS.Timer>();
|
||||||
|
|
||||||
|
const onMouseEnter = useCallback(() => {
|
||||||
|
if (popoverRenderTimeout.current) {
|
||||||
|
clearTimeout(popoverRenderTimeout.current);
|
||||||
|
}
|
||||||
|
setIsOpen(true);
|
||||||
|
}, [setIsOpen]);
|
||||||
|
|
||||||
|
const onMouseLeave = useCallback(() => {
|
||||||
|
popoverRenderTimeout.current = setTimeout(() => {
|
||||||
|
setIsOpen(false);
|
||||||
|
}, 100);
|
||||||
|
}, [setIsOpen]);
|
||||||
|
|
||||||
|
const renderMarker = useCallback(() => {
|
||||||
|
if (!markerRef?.current) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const el = markerRef.current;
|
||||||
|
const elBBox = el.getBoundingClientRect();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipContainer
|
||||||
|
position={{ x: elBBox.left, y: elBBox.top + elBBox.height }}
|
||||||
|
offset={{ x: 0, y: 0 }}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
className={styles.tooltip}
|
||||||
|
>
|
||||||
|
<div ref={annotationPopoverRef} className={styles.wrapper}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<span className={styles.title}>{annotationEvent.title}</span>
|
||||||
|
{annotationEvent.time && <span className={styles.time}>{formatTime(annotationEvent.time)}</span>}
|
||||||
|
</div>
|
||||||
|
<div className={styles.body}>
|
||||||
|
{annotationEvent.text && <div dangerouslySetInnerHTML={{ __html: annotationEvent.text }} />}
|
||||||
|
<>
|
||||||
|
<HorizontalGroup spacing="xs" wrap>
|
||||||
|
{annotationEvent.tags?.map((t, i) => (
|
||||||
|
<Tag name={t} key={`${t}-${i}`} />
|
||||||
|
))}
|
||||||
|
</HorizontalGroup>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TooltipContainer>
|
||||||
|
);
|
||||||
|
}, [annotationEvent]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
ref={markerRef}
|
||||||
|
onMouseEnter={onMouseEnter}
|
||||||
|
onMouseLeave={onMouseLeave}
|
||||||
|
className={cx(
|
||||||
|
styles.markerWrapper,
|
||||||
|
css`
|
||||||
|
left: ${x - 8}px;
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={styles.marker} />
|
||||||
|
</div>
|
||||||
|
{isOpen && <Portal>{renderMarker()}</Portal>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnnotationMarkerStyles = (theme: GrafanaTheme) => {
|
||||||
|
const bg = theme.isDark ? theme.palette.dark2 : theme.palette.white;
|
||||||
|
const headerBg = theme.isDark ? theme.palette.dark9 : theme.palette.gray5;
|
||||||
|
const shadowColor = theme.isDark ? theme.palette.black : theme.palette.white;
|
||||||
|
|
||||||
|
return {
|
||||||
|
markerWrapper: css`
|
||||||
|
padding: 0 4px 4px 4px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
`,
|
||||||
|
marker: css`
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-bottom: 4px solid ${theme.palette.red};
|
||||||
|
pointer-events: none;
|
||||||
|
`,
|
||||||
|
wrapper: css`
|
||||||
|
background: ${bg};
|
||||||
|
border: 1px solid ${headerBg};
|
||||||
|
border-radius: ${theme.border.radius.md};
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: 0 0 20px ${shadowColor};
|
||||||
|
`,
|
||||||
|
tooltip: css`
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
`,
|
||||||
|
header: css`
|
||||||
|
background: ${headerBg};
|
||||||
|
padding: 6px 10px;
|
||||||
|
display: flex;
|
||||||
|
`,
|
||||||
|
title: css`
|
||||||
|
font-weight: ${theme.typography.weight.semibold};
|
||||||
|
padding-right: ${theme.spacing.md};
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-grow: 1;
|
||||||
|
`,
|
||||||
|
time: css`
|
||||||
|
color: ${theme.colors.textWeak};
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
top: 1px;
|
||||||
|
`,
|
||||||
|
body: css`
|
||||||
|
padding: ${theme.spacing.sm};
|
||||||
|
font-weight: ${theme.typography.weight.semibold};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
};
|
126
public/app/plugins/panel/graph3/plugins/AnnotationsPlugin.tsx
Normal file
126
public/app/plugins/panel/graph3/plugins/AnnotationsPlugin.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { AnnotationEvent, DataFrame, dateTimeFormat, systemDateFormats, TimeZone } from '@grafana/data';
|
||||||
|
import { usePlotContext, usePlotPluginContext, useTheme } from '@grafana/ui';
|
||||||
|
import { getAnnotationsFromData } from 'app/features/annotations/standardAnnotationSupport';
|
||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { css } from 'emotion';
|
||||||
|
import { AnnotationMarker } from './AnnotationMarker';
|
||||||
|
|
||||||
|
interface AnnotationsPluginProps {
|
||||||
|
annotations: DataFrame[];
|
||||||
|
timeZone: TimeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AnnotationsPlugin: React.FC<AnnotationsPluginProps> = ({ annotations, timeZone }) => {
|
||||||
|
const pluginId = 'AnnotationsPlugin';
|
||||||
|
const pluginsApi = usePlotPluginContext();
|
||||||
|
const plotContext = usePlotContext();
|
||||||
|
const annotationsRef = useRef<AnnotationEvent[] | null>(null);
|
||||||
|
const [renderCounter, setRenderCounter] = useState(0);
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const timeFormatter = useCallback(
|
||||||
|
(value: number) => {
|
||||||
|
return dateTimeFormat(value, {
|
||||||
|
format: systemDateFormats.fullDate,
|
||||||
|
timeZone,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[timeZone]
|
||||||
|
);
|
||||||
|
|
||||||
|
const annotationsData = useMemo(() => {
|
||||||
|
return getAnnotationsFromData(annotations);
|
||||||
|
}, [annotations]);
|
||||||
|
|
||||||
|
const annotationMarkers = useMemo(() => {
|
||||||
|
if (!plotContext || !plotContext?.u) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const markers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < annotationsData.length; i++) {
|
||||||
|
const annotation = annotationsData[i];
|
||||||
|
if (!annotation.time) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const xpos = plotContext.u.valToPos(annotation.time / 1000, 'x');
|
||||||
|
markers.push(
|
||||||
|
<AnnotationMarker
|
||||||
|
x={xpos}
|
||||||
|
key={`${annotation.time}-${i}`}
|
||||||
|
formatTime={timeFormatter}
|
||||||
|
annotationEvent={annotation}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={css`
|
||||||
|
position: absolute;
|
||||||
|
left: ${plotContext.u.bbox.left / window.devicePixelRatio}px;
|
||||||
|
top: ${plotContext.u.bbox.top / window.devicePixelRatio +
|
||||||
|
plotContext.u.bbox.height / window.devicePixelRatio}px;
|
||||||
|
width: ${plotContext.u.bbox.width / window.devicePixelRatio}px;
|
||||||
|
height: 14px;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{markers}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}, [annotationsData, timeFormatter, plotContext, renderCounter]);
|
||||||
|
|
||||||
|
// For uPlot plugin to have access to lates annotation data we need to update the data ref
|
||||||
|
useEffect(() => {
|
||||||
|
annotationsRef.current = annotationsData;
|
||||||
|
}, [annotationsData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unregister = pluginsApi.registerPlugin({
|
||||||
|
id: pluginId,
|
||||||
|
hooks: {
|
||||||
|
// Render annotation lines on the canvas
|
||||||
|
draw: u => {
|
||||||
|
/**
|
||||||
|
* We cannot rely on state value here, as it would require this effect to be dependent on the state value.
|
||||||
|
* This would make the plugin re-register making the entire plot to reinitialise. ref is the way to go :)
|
||||||
|
*/
|
||||||
|
if (!annotationsRef.current) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ctx = u.ctx;
|
||||||
|
if (!ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < annotationsRef.current.length; i++) {
|
||||||
|
const annotation = annotationsRef.current[i];
|
||||||
|
if (!annotation.time) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const xpos = u.valToPos(annotation.time / 1000, 'x', true);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = theme.palette.red;
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
ctx.moveTo(xpos, u.bbox.top);
|
||||||
|
ctx.lineTo(xpos, u.bbox.top + u.bbox.height);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
setRenderCounter(c => c + 1);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unregister();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!plotContext || !plotContext.u || !plotContext.canvas) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>{annotationMarkers}</div>;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user