mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: features (#1711)
* feat: initial uPlot graph * feat: add zoom/pan for graph * fix: add zoom by ctrl/mac * fix: remove unused code * feat: add toggle cache for fetch * feat: add fix y-axis limits * fix: stop point events while panning * fix: change getting cursor position when scaling * feat: add cursor tooltip to graph * fix: uninstall chart.js * fix: change link for create an issue * fix: set default cache value to true * app/vmalert: follow-up after0e2486df56
* docs/CHANGELOG.md: document5416e18007
* app/vmui: `make vmui-update` Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
146a5b504c
commit
a3e09a57c2
31 changed files with 346 additions and 272 deletions
|
@ -513,7 +513,7 @@ The shortlist of configuration flags is the following:
|
|||
-remoteRead.tlsServerName string
|
||||
Optional TLS server name to use for connections to -remoteRead.url. By default the server name from -remoteRead.url is used
|
||||
-remoteRead.url vmalert
|
||||
Optional URL to VictoriaMetrics or vmselect that will be used to restore alerts state. This configuration makes sense only if vmalert was configured with `remoteWrite.url` before and has been successfully persisted its state. E.g. http://127.0.0.1:8428
|
||||
Optional URL to VictoriaMetrics or vmselect that will be used to restore alerts state. This configuration makes sense only if vmalert was configured with `remoteWrite.url` before and has been successfully persisted its state. E.g. http://127.0.0.1:8428. See also -remoteRead.disablePathAppend
|
||||
-remoteWrite.basicAuth.password string
|
||||
Optional basic auth password for -remoteWrite.url
|
||||
-remoteWrite.basicAuth.passwordFile string
|
||||
|
|
|
@ -26,7 +26,7 @@ var (
|
|||
"By default system CA is used")
|
||||
tlsServerName = flag.String("remoteRead.tlsServerName", "", "Optional TLS server name to use for connections to -remoteRead.url. "+
|
||||
"By default the server name from -remoteRead.url is used")
|
||||
disablePathAppend = flag.Bool("remoteRead.disablePathAppend", false, "Whether to disable automatic appending of '/api/v1' path to the configured -remoteRead.url.")
|
||||
disablePathAppend = flag.Bool("remoteRead.disablePathAppend", false, "Whether to disable automatic appending of '/api/v1/query' path to the configured -remoteRead.url.")
|
||||
)
|
||||
|
||||
// Init creates a Querier from provided flag values.
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.cbb91dd8.chunk.css",
|
||||
"main.js": "./static/js/main.3fb3f4d1.chunk.js",
|
||||
"runtime-main.js": "./static/js/runtime-main.22ec3f63.js",
|
||||
"main.css": "./static/css/main.acc63211.chunk.css",
|
||||
"main.js": "./static/js/main.33166fff.chunk.js",
|
||||
"runtime-main.js": "./static/js/runtime-main.b765a534.js",
|
||||
"static/css/2.a684aa27.chunk.css": "./static/css/2.a684aa27.chunk.css",
|
||||
"static/js/2.72d7cb01.chunk.js": "./static/js/2.72d7cb01.chunk.js",
|
||||
"static/js/3.3cf0cbc4.chunk.js": "./static/js/3.3cf0cbc4.chunk.js",
|
||||
"static/js/2.856ca35b.chunk.js": "./static/js/2.856ca35b.chunk.js",
|
||||
"static/js/3.daeccd9c.chunk.js": "./static/js/3.daeccd9c.chunk.js",
|
||||
"index.html": "./index.html",
|
||||
"static/js/2.72d7cb01.chunk.js.LICENSE.txt": "./static/js/2.72d7cb01.chunk.js.LICENSE.txt"
|
||||
"static/js/2.856ca35b.chunk.js.LICENSE.txt": "./static/js/2.856ca35b.chunk.js.LICENSE.txt"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.22ec3f63.js",
|
||||
"static/js/runtime-main.b765a534.js",
|
||||
"static/css/2.a684aa27.chunk.css",
|
||||
"static/js/2.72d7cb01.chunk.js",
|
||||
"static/css/main.cbb91dd8.chunk.css",
|
||||
"static/js/main.3fb3f4d1.chunk.js"
|
||||
"static/js/2.856ca35b.chunk.js",
|
||||
"static/css/main.acc63211.chunk.css",
|
||||
"static/js/main.33166fff.chunk.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="./static/css/2.a684aa27.chunk.css" rel="stylesheet"><link href="./static/css/main.cbb91dd8.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"3cf0cbc4"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.72d7cb01.chunk.js"></script><script src="./static/js/main.3fb3f4d1.chunk.js"></script></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="./static/css/2.a684aa27.chunk.css" rel="stylesheet"><link href="./static/css/main.acc63211.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"daeccd9c"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="./static/js/2.856ca35b.chunk.js"></script><script src="./static/js/main.33166fff.chunk.js"></script></body></html>
|
|
@ -1 +1 @@
|
|||
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.MuiAccordionSummary-content{margin:10px 0!important}.cm-activeLine{background-color:inherit!important}.cm-wrap{border-radius:4px;border:1px solid #b9b9b9;font-size:10px}.one-line-scroll .cm-wrap{height:24px}.cm-content,.cm-gutter{min-height:51px}.one-line-scroll .cm-content,.one-line-scroll .cm-gutter{min-height:auto}.uplot .u-legend{display:grid;align-items:center;justify-content:start;text-align:left;margin-top:25px}.uplot .u-legend .u-series{font-size:12px}
|
||||
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.MuiAccordionSummary-content{margin:10px 0!important}.cm-activeLine{background-color:inherit!important}.cm-wrap{border-radius:4px;border:1px solid #b9b9b9;font-size:10px}.one-line-scroll .cm-wrap{height:24px}.cm-content,.cm-gutter{min-height:51px}.one-line-scroll .cm-content,.one-line-scroll .cm-gutter{min-height:auto}.uplot .u-legend{display:grid;align-items:center;justify-content:start;text-align:left;margin-top:25px}.uplot .u-legend .u-series{font-size:12px}.u-tooltip{position:absolute;display:none;grid-gap:12px;max-width:300px;padding:8px;border-radius:4px;background:rgba(57,57,57,.9);color:#fff;font-size:10px;line-height:1.4em;font-weight:500;word-wrap:break-word;font-family:monospace;pointer-events:none;z-index:100}.u-tooltip-data{display:flex;flex-wrap:wrap;align-items:center;font-size:11px}.u-tooltip__info{display:grid;grid-gap:4px}.u-tooltip__marker{width:12px;height:12px;margin-right:4px}
|
File diff suppressed because one or more lines are too long
2
app/vmselect/vmui/static/js/2.856ca35b.chunk.js
Normal file
2
app/vmselect/vmui/static/js/2.856ca35b.chunk.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -4,13 +4,6 @@ object-assign
|
|||
@license MIT
|
||||
*/
|
||||
|
||||
/*!
|
||||
* chartjs-adapter-date-fns v2.0.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2021 chartjs-adapter-date-fns Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
|
@ -34,12 +27,6 @@ PERFORMANCE OF THIS SOFTWARE.
|
|||
* http://adamwdraper.github.com/Numeral-js/
|
||||
*/
|
||||
|
||||
/*! Hammer.JS - v2.0.7 - 2016-04-22
|
||||
* http://hammerjs.github.io/
|
||||
*
|
||||
* Copyright (c) 2016 Jorik Tangelder;
|
||||
* Licensed under the MIT license */
|
||||
|
||||
/*! exports provided: default */
|
||||
|
||||
/*! exports provided: optionsUpdateState, dataMatch */
|
|
@ -1 +1 @@
|
|||
(this.webpackJsonpvmui=this.webpackJsonpvmui||[]).push([[3],{271:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return l})),e.d(n,"getFCP",(function(){return g})),e.d(n,"getFID",(function(){return h})),e.d(n,"getLCP",(function(){return y})),e.d(n,"getTTFB",(function(){return F}));var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:r(),isFinal:!1}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},s=!1,c=!1,d=function(t){s=!t.persisted},f=function(){addEventListener("pagehide",d),addEventListener("beforeunload",(function(){}))},p=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(f(),c=!0),addEventListener("visibilitychange",(function(n){var e=n.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:e,isUnloading:s})}),{capture:!0,once:n})},v=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||"hidden"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},l=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),n())},r=u("layout-shift",a);r&&(n=v(t,i,r,e),p((function(t){var e=t.isUnloading;r.takeRecords().map(a),e&&(i.isFinal=!0),n()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,p((function(t){var n=t.timeStamp;return i=n}),!0)),{get timeStamp(){return i}}},g=function(t){var n,e=o("FCP"),i=m(),a=u("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));a&&(n=v(t,e,a))},h=function(t){var n=o("FID"),e=m(),i=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,r())},a=u("first-input",i),r=v(t,n,a);a?p((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var e=t.startTime;e<a.timeStamp?(i.value=e,i.entries.push(t)):i.isFinal=!0,n()},s=u("largest-contentful-paint",r);if(s){n=v(t,i,s,e);var c=function(){i.isFinal||(s.takeRecords().map(r),i.isFinal=!0,n())};S().then(c),p(c,!0)}},F=function(t){var n,e=o("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
|
||||
(this.webpackJsonpvmui=this.webpackJsonpvmui||[]).push([[3],{266:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return l})),e.d(n,"getFCP",(function(){return g})),e.d(n,"getFID",(function(){return h})),e.d(n,"getLCP",(function(){return y})),e.d(n,"getTTFB",(function(){return F}));var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:r(),isFinal:!1}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},s=!1,c=!1,d=function(t){s=!t.persisted},f=function(){addEventListener("pagehide",d),addEventListener("beforeunload",(function(){}))},p=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(f(),c=!0),addEventListener("visibilitychange",(function(n){var e=n.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:e,isUnloading:s})}),{capture:!0,once:n})},v=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||"hidden"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},l=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),n())},r=u("layout-shift",a);r&&(n=v(t,i,r,e),p((function(t){var e=t.isUnloading;r.takeRecords().map(a),e&&(i.isFinal=!0),n()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,p((function(t){var n=t.timeStamp;return i=n}),!0)),{get timeStamp(){return i}}},g=function(t){var n,e=o("FCP"),i=m(),a=u("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));a&&(n=v(t,e,a))},h=function(t){var n=o("FID"),e=m(),i=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,r())},a=u("first-input",i),r=v(t,n,a);a?p((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var e=t.startTime;e<a.timeStamp?(i.value=e,i.entries.push(t)):i.isFinal=!0,n()},s=u("largest-contentful-paint",r);if(s){n=v(t,i,s,e);var c=function(){i.isFinal||(s.takeRecords().map(r),i.isFinal=!0,n())};S().then(c),p(c,!0)}},F=function(t){var n,e=o("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
|
1
app/vmselect/vmui/static/js/main.33166fff.chunk.js
Normal file
1
app/vmselect/vmui/static/js/main.33166fff.chunk.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"3cf0cbc4"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
|
||||
!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],s=0,p=[];s<a.length;s++)i=a[s],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"daeccd9c"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="./",i.oe=function(e){throw console.error(e),e};var a=this.webpackJsonpvmui=this.webpackJsonpvmui||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
|
110
app/vmui/packages/vmui/package-lock.json
generated
110
app/vmui/packages/vmui/package-lock.json
generated
|
@ -17,7 +17,6 @@
|
|||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.1.2",
|
||||
"@testing-library/user-event": "^12.2.2",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
|
@ -27,9 +26,6 @@
|
|||
"@types/react": "^16.9.56",
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"@types/react-measure": "^2.0.6",
|
||||
"chart.js": "^3.5.1",
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-plugin-zoom": "^1.1.1",
|
||||
"codemirror-promql": "^0.10.2",
|
||||
"date-fns": "^2.23.0",
|
||||
"dayjs": "^1.10.4",
|
||||
|
@ -38,7 +34,6 @@
|
|||
"numeral": "^2.0.6",
|
||||
"qs": "^6.5.2",
|
||||
"react": "^17.0.1",
|
||||
"react-chartjs-2": "^3.0.5",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-measure": "^2.5.2",
|
||||
"react-scripts": "4.0.0",
|
||||
|
@ -2869,14 +2864,6 @@
|
|||
"@babel/types": "^7.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chart.js": {
|
||||
"version": "2.9.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.34.tgz",
|
||||
"integrity": "sha512-CtZVk+kh1IN67dv+fB0CWmCLCRrDJgqOj15qPic2B1VCMovNO6B7Vhf/TgPpNscjhAL1j+qUntDMWb9A4ZmPTg==",
|
||||
"dependencies": {
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.5.tgz",
|
||||
|
@ -5069,30 +5056,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz",
|
||||
"integrity": "sha512-m5kzt72I1WQ9LILwQC4syla/LD/N413RYv2Dx2nnTkRS9iv/ey1xLTt0DnPc/eWV4zI+BgEgDYBIzbQhZHc/PQ=="
|
||||
},
|
||||
"node_modules/chartjs-adapter-date-fns": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz",
|
||||
"integrity": "sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw==",
|
||||
"peerDependencies": {
|
||||
"chart.js": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-plugin-zoom": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-1.1.1.tgz",
|
||||
"integrity": "sha512-1q54WOzK7FtAjkbemQeqvmFUV0btNYIQny2HbQ6Awq9wUtCz7Zmj6vIgp3C1DYMQwN0nqgpC3vnApqiwI7cSdQ==",
|
||||
"dependencies": {
|
||||
"hammerjs": "^2.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"chart.js": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/check-types": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
|
||||
|
@ -8956,14 +8919,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/handle-thing": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||
|
@ -12496,14 +12451,6 @@
|
|||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
|
@ -15218,18 +15165,6 @@
|
|||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/react-chartjs-2": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-3.0.5.tgz",
|
||||
"integrity": "sha512-fYr4E82agaZi9IFMe5GtOZ6WE/HWdxy/KywLNOzXsqgPkD2oo1IlrQLKMLUki/2UXko3p95TR2L8Q2rEss/opQ==",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"chart.js": "^3.5.0",
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dev-utils": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||
|
@ -22907,14 +22842,6 @@
|
|||
"@babel/types": "^7.3.0"
|
||||
}
|
||||
},
|
||||
"@types/chart.js": {
|
||||
"version": "2.9.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.34.tgz",
|
||||
"integrity": "sha512-CtZVk+kh1IN67dv+fB0CWmCLCRrDJgqOj15qPic2B1VCMovNO6B7Vhf/TgPpNscjhAL1j+qUntDMWb9A4ZmPTg==",
|
||||
"requires": {
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.5.tgz",
|
||||
|
@ -24703,25 +24630,6 @@
|
|||
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
||||
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
|
||||
},
|
||||
"chart.js": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.1.tgz",
|
||||
"integrity": "sha512-m5kzt72I1WQ9LILwQC4syla/LD/N413RYv2Dx2nnTkRS9iv/ey1xLTt0DnPc/eWV4zI+BgEgDYBIzbQhZHc/PQ=="
|
||||
},
|
||||
"chartjs-adapter-date-fns": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz",
|
||||
"integrity": "sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw==",
|
||||
"requires": {}
|
||||
},
|
||||
"chartjs-plugin-zoom": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-1.1.1.tgz",
|
||||
"integrity": "sha512-1q54WOzK7FtAjkbemQeqvmFUV0btNYIQny2HbQ6Awq9wUtCz7Zmj6vIgp3C1DYMQwN0nqgpC3vnApqiwI7cSdQ==",
|
||||
"requires": {
|
||||
"hammerjs": "^2.0.8"
|
||||
}
|
||||
},
|
||||
"check-types": {
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
|
||||
|
@ -27796,11 +27704,6 @@
|
|||
"pify": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
|
||||
},
|
||||
"handle-thing": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
|
||||
|
@ -30561,11 +30464,6 @@
|
|||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
|
@ -32763,14 +32661,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-chartjs-2": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-3.0.5.tgz",
|
||||
"integrity": "sha512-fYr4E82agaZi9IFMe5GtOZ6WE/HWdxy/KywLNOzXsqgPkD2oo1IlrQLKMLUki/2UXko3p95TR2L8Q2rEss/opQ==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
},
|
||||
"react-dev-utils": {
|
||||
"version": "11.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz",
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.1.2",
|
||||
"@testing-library/user-event": "^12.2.2",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
|
@ -23,9 +22,6 @@
|
|||
"@types/react": "^16.9.56",
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"@types/react-measure": "^2.0.6",
|
||||
"chart.js": "^3.5.1",
|
||||
"chartjs-adapter-date-fns": "^2.0.0",
|
||||
"chartjs-plugin-zoom": "^1.1.1",
|
||||
"codemirror-promql": "^0.10.2",
|
||||
"date-fns": "^2.23.0",
|
||||
"dayjs": "^1.10.4",
|
||||
|
@ -34,7 +30,6 @@
|
|||
"numeral": "^2.0.6",
|
||||
"qs": "^6.5.2",
|
||||
"react": "^17.0.1",
|
||||
"react-chartjs-2": "^3.0.5",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-measure": "^2.5.2",
|
||||
"react-scripts": "4.0.0",
|
||||
|
|
|
@ -3,6 +3,7 @@ import {SnackbarProvider} from "./contexts/Snackbar";
|
|||
import HomeLayout from "./components/Home/HomeLayout";
|
||||
import {StateProvider} from "./state/common/StateContext";
|
||||
import {AuthStateProvider} from "./state/auth/AuthStateContext";
|
||||
import {GraphStateProvider} from "./state/graph/GraphStateContext";
|
||||
import {createMuiTheme, MuiThemeProvider} from "@material-ui/core";
|
||||
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
|
@ -26,9 +27,11 @@ const App: FC = () => {
|
|||
<MuiThemeProvider theme={THEME}> {/* Material UI theme customization */}
|
||||
<StateProvider> {/* Serialized into query string, common app settings */}
|
||||
<AuthStateProvider> {/* Auth related info - optionally persisted to Local Storage */}
|
||||
<SnackbarProvider> {/* Display various snackbars */}
|
||||
<HomeLayout/>
|
||||
</SnackbarProvider>
|
||||
<GraphStateProvider> {/* Graph settings */}
|
||||
<SnackbarProvider> {/* Display various snackbars */}
|
||||
<HomeLayout/>
|
||||
</SnackbarProvider>
|
||||
</GraphStateProvider>
|
||||
</AuthStateProvider>
|
||||
</StateProvider>
|
||||
</MuiThemeProvider>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {TimeParams} from "../types";
|
||||
|
||||
export const getQueryRangeUrl = (server: string, query: string, period: TimeParams): string =>
|
||||
`${server}/api/v1/query_range?query=${encodeURIComponent(query)}&start=${period.start}&end=${period.end}&step=${period.step}`;
|
||||
export const getQueryRangeUrl = (server: string, query: string, period: TimeParams, nocache: boolean): string =>
|
||||
`${server}/api/v1/query_range?query=${encodeURIComponent(query)}&start=${period.start}&end=${period.end}&step=${period.step}${nocache ? "&nocache=1" : ""}`;
|
||||
|
||||
export const getQueryUrl = (server: string, query: string, period: TimeParams): string =>
|
||||
`${server}/api/v1/query?query=${encodeURIComponent(query)}&start=${period.start}&end=${period.end}&step=${period.step}`;
|
||||
|
|
|
@ -20,25 +20,40 @@ import SecurityIcon from "@material-ui/icons/Security";
|
|||
import {AuthDialog} from "./AuthDialog";
|
||||
import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline";
|
||||
import Portal from "@material-ui/core/Portal";
|
||||
import Popover from "@material-ui/core/Popover";
|
||||
import SettingsIcon from "@material-ui/icons/Settings";
|
||||
import {saveToStorage} from "../../../utils/storage";
|
||||
import {useGraphDispatch, useGraphState} from "../../../state/graph/GraphStateContext";
|
||||
import debounce from "lodash.debounce";
|
||||
|
||||
const QueryConfigurator: FC = () => {
|
||||
|
||||
const {serverUrl, query, time: {duration}} = useAppState();
|
||||
const {serverUrl, query, time: {duration}, queryControls: {autocomplete, nocache}} = useAppState();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const {queryControls: {autocomplete}} = useAppState();
|
||||
const onChangeAutocomplete = () => {
|
||||
dispatch({type: "TOGGLE_AUTOCOMPLETE"});
|
||||
saveToStorage("AUTOCOMPLETE", !autocomplete);
|
||||
};
|
||||
const onChangeCache = () => {
|
||||
dispatch({type: "NO_CACHE"});
|
||||
saveToStorage("NO_CACHE", !nocache);
|
||||
};
|
||||
|
||||
const {yaxis} = useGraphState();
|
||||
const graphDispatch = useGraphDispatch();
|
||||
|
||||
const onChangeYaxisLimits = () => {
|
||||
graphDispatch({type: "TOGGLE_ENABLE_YAXIS_LIMITS"});
|
||||
};
|
||||
|
||||
const setMinLimit = ({target: {value}}: {target: {value: string}}) => {
|
||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: [+value, yaxis.limits.range[1]]});
|
||||
};
|
||||
const setMaxLimit = ({target: {value}}: {target: {value: string}}) => {
|
||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: [yaxis.limits.range[0], +value]});
|
||||
};
|
||||
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const [popoverOpen, setPopoverOpen] = useState(false);
|
||||
const refSettings = useRef<SVGGElement | any>(null);
|
||||
|
||||
const queryContainer = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -57,9 +72,7 @@ const QueryConfigurator: FC = () => {
|
|||
aria-controls="panel1a-content"
|
||||
id="panel1a-header"
|
||||
>
|
||||
<Box mr={2}>
|
||||
<Typography variant="h6" component="h2">Query Configuration</Typography>
|
||||
</Box>
|
||||
<Box mr={2}><Typography variant="h6" component="h2">Query Configuration</Typography></Box>
|
||||
<Box flexGrow={1} onClick={e => e.stopPropagation()} onFocusCapture={e => e.stopPropagation()}>
|
||||
<Portal disablePortal={!expanded} container={queryContainer.current}>
|
||||
<QueryEditor server={serverUrl} query={query} oneLiner={!expanded} autocomplete={autocomplete}
|
||||
|
@ -71,51 +84,23 @@ const QueryConfigurator: FC = () => {
|
|||
<AccordionDetails>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Box>
|
||||
<Box py={2} display="flex" alignItems="center">
|
||||
<Box display="grid" gridGap={16}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<TextField variant="outlined" fullWidth label="Server URL" value={serverUrl}
|
||||
inputProps={{
|
||||
style: {fontFamily: "Monospace"}
|
||||
}}
|
||||
inputProps={{style: {fontFamily: "Monospace"}}}
|
||||
onChange={onSetServer}/>
|
||||
<Box ml={1}>
|
||||
<Tooltip title="Execute Query">
|
||||
<IconButton onClick={onRunQuery}>
|
||||
<PlayCircleOutlineIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={onRunQuery}><PlayCircleOutlineIcon /></IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<Box>
|
||||
<Tooltip title="Request Auth Settings">
|
||||
<IconButton onClick={() => setDialogOpen(true)}>
|
||||
<SecurityIcon/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => setDialogOpen(true)}><SecurityIcon/></IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box py={2} display="flex">
|
||||
<Box flexGrow={1} mr={2}>
|
||||
{/* for portal QueryEditor */}
|
||||
<div ref={queryContainer} />
|
||||
</Box>
|
||||
<div>
|
||||
<Tooltip title="Query Editor Settings">
|
||||
<IconButton onClick={() => setPopoverOpen(!popoverOpen)}>
|
||||
<SettingsIcon ref={refSettings}/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Popover open={popoverOpen} transformOrigin={{vertical: -20, horizontal: "left"}}
|
||||
onClose={() => setPopoverOpen(false)}
|
||||
anchorEl={refSettings.current}>
|
||||
<Box p={2}>
|
||||
{<FormControlLabel
|
||||
control={<Switch size="small" checked={autocomplete} onChange={onChangeAutocomplete}/>}
|
||||
label="Autocomplete"
|
||||
/>}
|
||||
</Box>
|
||||
</Popover>
|
||||
</div>
|
||||
</Box>
|
||||
<Box flexGrow={1} ><div ref={queryContainer} />{/* for portal QueryEditor */}</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={8} md={6} >
|
||||
|
@ -124,12 +109,35 @@ const QueryConfigurator: FC = () => {
|
|||
borderColor: "#b9b9b9",
|
||||
borderStyle: "solid",
|
||||
borderWidth: "1px",
|
||||
height: "calc(100% - 18px)",
|
||||
marginTop: "16px"
|
||||
height: "100%",
|
||||
}}>
|
||||
<TimeSelector setDuration={onSetDuration} duration={duration}/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Box px={1} display="flex" alignItems="center" minHeight={52}>
|
||||
<Box><FormControlLabel
|
||||
control={<Switch size="small" checked={autocomplete} onChange={onChangeAutocomplete}/>}
|
||||
label="Enable autocomplete"
|
||||
/></Box>
|
||||
<Box ml={4}><FormControlLabel
|
||||
control={<Switch size="small" checked={!nocache} onChange={onChangeCache}/>}
|
||||
label="Enable cache"
|
||||
/></Box>
|
||||
<Box ml={4} display="flex" alignItems="center">
|
||||
<FormControlLabel
|
||||
control={<Switch size="small" checked={yaxis.limits.enable} onChange={onChangeYaxisLimits}/>}
|
||||
label="fix the limits for y-axis"
|
||||
/>
|
||||
{yaxis.limits.enable && <Box display="grid" gridTemplateColumns="120px 120px" gridGap={10}>
|
||||
<TextField label="Min" type="number" size="small" variant="outlined"
|
||||
defaultValue={yaxis.limits.range[0]} onChange={debounce(setMinLimit, 750)}/>
|
||||
<TextField label="Max" type="number" size="small" variant="outlined"
|
||||
defaultValue={yaxis.limits.range[1]} onChange={debounce(setMaxLimit, 750)}/>
|
||||
</Box>}
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
|
|
@ -13,7 +13,7 @@ export const useFetchQuery = (): {
|
|||
liveData?: InstantMetricResult[],
|
||||
error?: string,
|
||||
} => {
|
||||
const {query, displayType, serverUrl, time: {period}} = useAppState();
|
||||
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache}} = useAppState();
|
||||
|
||||
const {basicData, bearerData, authMethod} = useAuthState();
|
||||
|
||||
|
@ -43,7 +43,7 @@ export const useFetchQuery = (): {
|
|||
const duration = (period.end - period.start)/2;
|
||||
const doublePeriod = {...period, start: period.start - duration, end: period.end + duration};
|
||||
return displayType === "chart"
|
||||
? getQueryRangeUrl(serverUrl, query, doublePeriod)
|
||||
? getQueryRangeUrl(serverUrl, query, doublePeriod, nocache)
|
||||
: getQueryUrl(serverUrl, query, period);
|
||||
} else {
|
||||
setError("Please provide a valid URL");
|
||||
|
|
|
@ -39,7 +39,7 @@ const HomeLayout: FC = () => {
|
|||
top: "40px",
|
||||
opacity: ".4"
|
||||
}}>
|
||||
<Link color="inherit" href="https://github.com/VictoriaMetrics/vmui/issues/new" target="_blank">
|
||||
<Link color="inherit" href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new" target="_blank">
|
||||
Create an issue
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, {FC} from "react";
|
||||
import {MetricResult} from "../../../api/types";
|
||||
import LineChart from "../../LineChart/LineChart";
|
||||
import "../../../utils/chartjs-register-plugins";
|
||||
|
||||
export interface GraphViewProps {
|
||||
data?: MetricResult[];
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import React, {FC, useEffect, useMemo, useRef, useState} from "react";
|
||||
import {getNameForMetric} from "../../utils/metric";
|
||||
import "chartjs-adapter-date-fns";
|
||||
import {useAppDispatch, useAppState} from "../../state/common/StateContext";
|
||||
import {GraphViewProps} from "../Home/Views/GraphView";
|
||||
import uPlot, {AlignedData as uPlotData, Options as uPlotOptions, Series as uPlotSeries} from "uplot";
|
||||
|
@ -8,76 +6,67 @@ import UplotReact from "uplot-react";
|
|||
import "uplot/dist/uPlot.min.css";
|
||||
import numeral from "numeral";
|
||||
import "./legend.css";
|
||||
import "./tooltip.css";
|
||||
import {useGraphDispatch, useGraphState} from "../../state/graph/GraphStateContext";
|
||||
import {getDataChart, getLimitsTimes, getLimitsYaxis, getSeries, setTooltip} from "../../utils/uPlot";
|
||||
|
||||
const LineChart: FC<GraphViewProps> = ({data = []}) => {
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const {time: {period}} = useAppState();
|
||||
const [dataChart, setDataChart] = useState<uPlotData>();
|
||||
const [series, setSeries] = useState<uPlotSeries[]>([]);
|
||||
const [scale, setScale] = useState({min: period.start, max: period.end});
|
||||
const refContainer = useRef<HTMLDivElement>(null);
|
||||
const [isPanning, setIsPanning] = useState(false);
|
||||
const [zoomPos, setZoomPos] = useState(0);
|
||||
const tooltipIdx = {seriesIdx: 1, dataIdx: 0};
|
||||
const tooltipOffset = {left: 0, top: 0};
|
||||
|
||||
const getColorByName = (str: string): string => {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
const {yaxis} = useGraphState();
|
||||
const graphDispatch = useGraphDispatch();
|
||||
const setStateLimits = (range: [number, number]) => {
|
||||
if (!yaxis.limits.enable || (yaxis.limits.range.every(item => !item))) {
|
||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: range});
|
||||
}
|
||||
let colour = "#";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const value = (hash >> (i * 8)) & 0xFF;
|
||||
colour += ("00" + value.toString(16)).substr(-2);
|
||||
}
|
||||
return colour;
|
||||
};
|
||||
|
||||
const times = useMemo(() => {
|
||||
const allTimes = data.map(d => d.values.map(v => v[0])).flat();
|
||||
const start = Math.min(...allTimes);
|
||||
const end = Math.max(...allTimes);
|
||||
const [start, end] = getLimitsTimes(data);
|
||||
const output = [];
|
||||
for (let i = start; i < end; i += period.step || 1) {
|
||||
output.push(i);
|
||||
}
|
||||
for (let i = start; i < end; i += period.step || 1) { output.push(i); }
|
||||
return output;
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
const values = data.map(d => times.map(t => {
|
||||
const v = d.values.find(v => v[0] === t);
|
||||
return v ? +v[1] : null;
|
||||
}));
|
||||
const seriesValues = data.map(d => ({
|
||||
label: getNameForMetric(d),
|
||||
width: 1,
|
||||
font: "11px Arial",
|
||||
stroke: getColorByName(getNameForMetric(d))}));
|
||||
setSeries([{}, ...seriesValues]);
|
||||
setDataChart([times, ...values]);
|
||||
}, [data]);
|
||||
const series = useMemo((): uPlotSeries[] => getSeries(data), [data]);
|
||||
|
||||
const dataChart = useMemo((): uPlotData => getDataChart(data, times), [data]);
|
||||
|
||||
const tooltip = document.createElement("div");
|
||||
tooltip.className = "u-tooltip";
|
||||
|
||||
const onReadyChart = (u: uPlot) => {
|
||||
const factor = 0.85;
|
||||
tooltipOffset.left = parseFloat(u.over.style.left);
|
||||
tooltipOffset.top = parseFloat(u.over.style.top);
|
||||
u.root.querySelector(".u-wrap")?.appendChild(tooltip);
|
||||
|
||||
// wheel drag pan
|
||||
u.over.addEventListener("mousedown", e => {
|
||||
if (e.button !== 0) return;
|
||||
setIsPanning(true);
|
||||
e.preventDefault();
|
||||
const left0 = e.clientX;
|
||||
const scXMin0 = u.scales.x.min || 1;
|
||||
const scXMax0 = u.scales.x.max || 1;
|
||||
const xUnitsPerPx = u.posToVal(1, "x") - u.posToVal(0, "x");
|
||||
|
||||
const onmove = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const dx = xUnitsPerPx * (e.clientX - left0);
|
||||
const min = scXMin0 - dx;
|
||||
const max = scXMax0 - dx;
|
||||
const dx = (u.posToVal(1, "x") - u.posToVal(0, "x")) * (e.clientX - left0);
|
||||
const min = (u.scales.x.min || 1) - dx;
|
||||
const max = (u.scales.x.max || 1) - dx;
|
||||
u.setScale("x", {min, max});
|
||||
setScale({min, max});
|
||||
};
|
||||
|
||||
const onup = () => {
|
||||
setIsPanning(false);
|
||||
document.removeEventListener("mousemove", onmove);
|
||||
document.removeEventListener("mouseup", onup);
|
||||
};
|
||||
|
@ -91,12 +80,11 @@ const LineChart: FC<GraphViewProps> = ({data = []}) => {
|
|||
if (!e.ctrlKey && !e.metaKey) return;
|
||||
e.preventDefault();
|
||||
const {width} = u.over.getBoundingClientRect();
|
||||
const {left = width/2} = u.cursor;
|
||||
const leftPct = left/width;
|
||||
const xVal = u.posToVal(left, "x");
|
||||
if (u.cursor.left && u.cursor.left > 0) setZoomPos(u.cursor.left);
|
||||
const xVal = u.posToVal(zoomPos, "x");
|
||||
const oxRange = (u.scales.x.max || 0) - (u.scales.x.min || 0);
|
||||
const nxRange = e.deltaY < 0 ? oxRange * factor : oxRange / factor;
|
||||
const min = xVal - leftPct * nxRange;
|
||||
const min = xVal - (zoomPos/width) * nxRange;
|
||||
const max = min + nxRange;
|
||||
u.batch(() => {
|
||||
u.setScale("x", {min, max});
|
||||
|
@ -105,7 +93,25 @@ const LineChart: FC<GraphViewProps> = ({data = []}) => {
|
|||
});
|
||||
};
|
||||
|
||||
useEffect(() => {setScale({min: period.start, max: period.end});}, [period]);
|
||||
const setCursor = (u: uPlot) => {
|
||||
if (tooltipIdx.dataIdx === u.cursor.idx) return;
|
||||
tooltipIdx.dataIdx = u.cursor.idx || 0;
|
||||
if (tooltipIdx.seriesIdx && tooltipIdx.dataIdx) {
|
||||
setTooltip({u, tooltipIdx, data, series, tooltip, tooltipOffset});
|
||||
}
|
||||
};
|
||||
|
||||
const seriesFocus = (u: uPlot, sidx: (number | null)) => {
|
||||
if (tooltipIdx.seriesIdx === sidx) return;
|
||||
tooltipIdx.seriesIdx = sidx || 0;
|
||||
sidx && tooltipIdx.dataIdx
|
||||
? setTooltip({u, tooltipIdx, data, series, tooltip, tooltipOffset})
|
||||
: tooltip.style.display = "none";
|
||||
};
|
||||
|
||||
useEffect(() => { setStateLimits(getLimitsYaxis(data)); }, [data]);
|
||||
|
||||
useEffect(() => { setScale({min: period.start, max: period.end}); }, [period]);
|
||||
|
||||
useEffect(() => {
|
||||
const duration = (period.end - period.start)/3;
|
||||
|
@ -119,12 +125,8 @@ const LineChart: FC<GraphViewProps> = ({data = []}) => {
|
|||
width: refContainer.current ? refContainer.current.offsetWidth : 400,
|
||||
height: 500,
|
||||
series: series,
|
||||
plugins: [{
|
||||
hooks: {
|
||||
ready: onReadyChart
|
||||
}
|
||||
}],
|
||||
cursor: {drag: {x: false, y: false}},
|
||||
plugins: [{hooks: {ready: onReadyChart, setCursor, setSeries: seriesFocus}}],
|
||||
cursor: {drag: {x: false, y: false}, focus: {prox: 30}},
|
||||
axes: [
|
||||
{space: 80},
|
||||
{
|
||||
|
@ -133,14 +135,14 @@ const LineChart: FC<GraphViewProps> = ({data = []}) => {
|
|||
values: (self, ticks) => ticks.map(n => n > 1000 ? numeral(n).format("0.0a") : n)
|
||||
}
|
||||
],
|
||||
scales: {x: {range: () => [scale.min, scale.max]}}
|
||||
scales: {
|
||||
x: {range: () => [scale.min, scale.max]},
|
||||
y: {range: (self, min, max) => yaxis.limits.enable ? yaxis.limits.range : [min, max]}
|
||||
}
|
||||
};
|
||||
|
||||
return <div ref={refContainer}>
|
||||
{dataChart && <UplotReact
|
||||
options={options}
|
||||
data={dataChart}
|
||||
/>}
|
||||
return <div ref={refContainer} style={{pointerEvents: isPanning ? "none" : "auto"}}>
|
||||
{dataChart && <UplotReact options={options} data={dataChart}/>}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
35
app/vmui/packages/vmui/src/components/LineChart/tooltip.css
Normal file
35
app/vmui/packages/vmui/src/components/LineChart/tooltip.css
Normal file
|
@ -0,0 +1,35 @@
|
|||
.u-tooltip {
|
||||
position: absolute;
|
||||
display: none;
|
||||
grid-gap: 12px;
|
||||
max-width: 300px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(57, 57, 57, 0.9);
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
line-height: 1.4em;
|
||||
font-weight: 500;
|
||||
word-wrap: break-word;
|
||||
font-family: monospace;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.u-tooltip-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.u-tooltip__info {
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
}
|
||||
|
||||
.u-tooltip__marker {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
|
@ -1,12 +1,6 @@
|
|||
import {DisplayType} from "../../components/Home/Configurator/DisplayTypeSwitch";
|
||||
import {TimeParams, TimePeriod} from "../../types";
|
||||
import {
|
||||
dateFromSeconds,
|
||||
formatDateToLocal,
|
||||
getDateNowUTC,
|
||||
getDurationFromPeriod,
|
||||
getTimeperiodForDuration
|
||||
} from "../../utils/time";
|
||||
import {dateFromSeconds, formatDateToLocal, getDateNowUTC, getDurationFromPeriod, getTimeperiodForDuration} from "../../utils/time";
|
||||
import {getFromStorage} from "../../utils/storage";
|
||||
import {getDefaultServer} from "../../utils/default-server-url";
|
||||
import {getQueryStringValue} from "../../utils/query-string";
|
||||
|
@ -23,7 +17,8 @@ export interface AppState {
|
|||
time: TimeState;
|
||||
queryControls: {
|
||||
autoRefresh: boolean;
|
||||
autocomplete: boolean
|
||||
autocomplete: boolean,
|
||||
nocache: boolean
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +33,7 @@ export type Action =
|
|||
| { type: "RUN_QUERY_TO_NOW"}
|
||||
| { type: "TOGGLE_AUTOREFRESH"}
|
||||
| { type: "TOGGLE_AUTOCOMPLETE"}
|
||||
| { type: "NO_CACHE"}
|
||||
|
||||
const duration = getQueryStringValue("g0.range_input", "1h") as string;
|
||||
const endInput = formatDateToLocal(getQueryStringValue("g0.end_input", getDateNowUTC()) as Date);
|
||||
|
@ -52,7 +48,8 @@ export const initialState: AppState = {
|
|||
},
|
||||
queryControls: {
|
||||
autoRefresh: false,
|
||||
autocomplete: getFromStorage("AUTOCOMPLETE") as boolean || false
|
||||
autocomplete: getFromStorage("AUTOCOMPLETE") as boolean || false,
|
||||
nocache: getFromStorage("NO_CACHE") as boolean || false,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,6 +118,14 @@ export function reducer(state: AppState, action: Action): AppState {
|
|||
autocomplete: !state.queryControls.autocomplete
|
||||
}
|
||||
};
|
||||
case "NO_CACHE":
|
||||
return {
|
||||
...state,
|
||||
queryControls: {
|
||||
...state.queryControls,
|
||||
nocache: !state.queryControls.nocache
|
||||
}
|
||||
};
|
||||
case "RUN_QUERY":
|
||||
return {
|
||||
...state,
|
||||
|
|
25
app/vmui/packages/vmui/src/state/graph/GraphStateContext.tsx
Normal file
25
app/vmui/packages/vmui/src/state/graph/GraphStateContext.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React, {createContext, Dispatch, FC, useContext, useMemo, useReducer} from "react";
|
||||
import {GraphAction, GraphState, initialGraphState, reducer} from "./reducer";
|
||||
|
||||
type GraphStateContextType = { state: GraphState, dispatch: Dispatch<GraphAction> };
|
||||
|
||||
export const GraphStateContext = createContext<GraphStateContextType>({} as GraphStateContextType);
|
||||
|
||||
export const useGraphState = (): GraphState => useContext(GraphStateContext).state;
|
||||
export const useGraphDispatch = (): Dispatch<GraphAction> => useContext(GraphStateContext).dispatch;
|
||||
|
||||
export const GraphStateProvider: FC = ({children}) => {
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, initialGraphState);
|
||||
|
||||
const contextValue = useMemo(() => {
|
||||
return { state, dispatch };
|
||||
}, [state, dispatch]);
|
||||
|
||||
|
||||
return <GraphStateContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</GraphStateContext.Provider>;
|
||||
};
|
||||
|
||||
|
49
app/vmui/packages/vmui/src/state/graph/reducer.ts
Normal file
49
app/vmui/packages/vmui/src/state/graph/reducer.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
export interface YaxisState {
|
||||
limits: {
|
||||
enable: boolean,
|
||||
range: [number, number]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GraphState {
|
||||
yaxis: YaxisState
|
||||
}
|
||||
|
||||
export type GraphAction =
|
||||
| { type: "TOGGLE_ENABLE_YAXIS_LIMITS" }
|
||||
| { type: "SET_YAXIS_LIMITS", payload: [number, number] }
|
||||
|
||||
export const initialGraphState: GraphState = {
|
||||
yaxis: {
|
||||
limits: {enable: false, range: [0, 0]}
|
||||
}
|
||||
};
|
||||
|
||||
export function reducer(state: GraphState, action: GraphAction): GraphState {
|
||||
switch (action.type) {
|
||||
case "TOGGLE_ENABLE_YAXIS_LIMITS":
|
||||
return {
|
||||
...state,
|
||||
yaxis: {
|
||||
...state.yaxis,
|
||||
limits: {
|
||||
...state.yaxis.limits,
|
||||
enable: !state.yaxis.limits.enable
|
||||
}
|
||||
}
|
||||
};
|
||||
case "SET_YAXIS_LIMITS":
|
||||
return {
|
||||
...state,
|
||||
yaxis: {
|
||||
...state.yaxis,
|
||||
limits: {
|
||||
...state.yaxis.limits,
|
||||
range: action.payload
|
||||
}
|
||||
}
|
||||
};
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
import {Chart} from "chart.js";
|
||||
import zoomPlugin from "chartjs-plugin-zoom";
|
||||
|
||||
Chart.register(zoomPlugin);
|
12
app/vmui/packages/vmui/src/utils/color.ts
Normal file
12
app/vmui/packages/vmui/src/utils/color.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const getColorFromString = (str: string): string => {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
let colour = "#";
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const value = (hash >> (i * 8)) & 0xFF;
|
||||
colour += ("00" + value.toString(16)).substr(-2);
|
||||
}
|
||||
return colour;
|
||||
};
|
|
@ -2,7 +2,8 @@ export type StorageKeys = "LAST_QUERY"
|
|||
| "BASIC_AUTH_DATA"
|
||||
| "BEARER_AUTH_DATA"
|
||||
| "AUTH_TYPE"
|
||||
| "AUTOCOMPLETE";
|
||||
| "AUTOCOMPLETE"
|
||||
| "NO_CACHE"
|
||||
|
||||
export const saveToStorage = (key: StorageKeys, value: string | boolean | Record<string, unknown>): void => {
|
||||
if (value) {
|
||||
|
|
64
app/vmui/packages/vmui/src/utils/uPlot.ts
Normal file
64
app/vmui/packages/vmui/src/utils/uPlot.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import uPlot, {AlignedData, Series} from "uplot";
|
||||
import {getColorFromString} from "./color";
|
||||
import dayjs from "dayjs";
|
||||
import {MetricResult} from "../api/types";
|
||||
import {getNameForMetric} from "./metric";
|
||||
|
||||
interface SetupTooltip {
|
||||
u: uPlot,
|
||||
data: MetricResult[],
|
||||
series: Series[],
|
||||
tooltip: HTMLDivElement,
|
||||
tooltipOffset: {left: number, top: number},
|
||||
tooltipIdx: {seriesIdx: number, dataIdx: number}
|
||||
}
|
||||
|
||||
export const setTooltip = ({ u, tooltipIdx, data, series, tooltip, tooltipOffset }: SetupTooltip) : void => {
|
||||
const {seriesIdx, dataIdx} = tooltipIdx;
|
||||
const dataSeries = u.data[seriesIdx][dataIdx];
|
||||
const dataTime = u.data[0][dataIdx];
|
||||
const metric = data[seriesIdx - 1]?.metric || {};
|
||||
const color = getColorFromString(series[seriesIdx].label || "");
|
||||
|
||||
const {width, height} = u.over.getBoundingClientRect();
|
||||
const top = u.valToPos((dataSeries || 0), "y");
|
||||
const lft = u.valToPos(dataTime, "x");
|
||||
const {width: tooltipWidth, height: tooltipHeight} = tooltip.getBoundingClientRect();
|
||||
const overflowX = lft + tooltipWidth >= width;
|
||||
const overflowY = top + tooltipHeight >= height;
|
||||
|
||||
tooltip.style.display = "grid";
|
||||
tooltip.style.top = `${tooltipOffset.top + top + 10 - (overflowY ? tooltipHeight + 10 : 0)}px`;
|
||||
tooltip.style.left = `${tooltipOffset.left + lft + 10 - (overflowX ? tooltipWidth + 20 : 0)}px`;
|
||||
const date = dayjs(new Date(dataTime * 1000)).format("YYYY-MM-DD HH:mm:ss:SSS (Z)");
|
||||
const info = Object.keys(metric).filter(k => k !== "__name__").map(k => `<div><b>${k}</b>: ${metric[k]}</div>`).join("");
|
||||
const marker = `<div class="u-tooltip__marker" style="background: ${color}"></div>`;
|
||||
tooltip.innerHTML = `<div>${date}</div>
|
||||
<div class="u-tooltip-data">
|
||||
${marker}${metric.__name__ || ""}: <b>${dataSeries}</b>
|
||||
</div>
|
||||
<div class="u-tooltip__info">${info}</div>`;
|
||||
};
|
||||
|
||||
export const getSeries = (data: MetricResult[]): Series[] => [{}, ...data.map(d => ({
|
||||
label: getNameForMetric(d),
|
||||
width: 1.5,
|
||||
stroke: getColorFromString(getNameForMetric(d))
|
||||
}))];
|
||||
|
||||
export const getLimitsTimes = (data: MetricResult[]): [number, number] => {
|
||||
const allTimes = data.map(d => d.values.map(v => v[0])).flat().sort((a,b) => a-b);
|
||||
return [allTimes[0], allTimes[allTimes.length - 1]];
|
||||
};
|
||||
|
||||
export const getLimitsYaxis = (data: MetricResult[]): [number, number] => {
|
||||
const allValues = data.map(d => d.values.map(v => +v[1])).flat().sort((a,b) => a-b);
|
||||
return [allValues[0], allValues[allValues.length - 1]];
|
||||
};
|
||||
|
||||
export const getDataChart = (data: MetricResult[], times: number[]): AlignedData => {
|
||||
return [times, ...data.map(d => times.map(t => {
|
||||
const v = d.values.find(v => v[0] === t);
|
||||
return v ? +v[1] : null;
|
||||
}))];
|
||||
};
|
|
@ -14,12 +14,15 @@ sort: 15
|
|||
* FEATURE: vmagent: properly calculate `scrape_series_added` metric for targets in [stream parsing mode](https://docs.victoriametrics.com/vmagent.html#stream-parsing-mode). Previously it was set to 0 in stream parsing mode. See [more details about this metric](https://prometheus.io/docs/concepts/jobs_instances/#automatically-generated-labels-and-time-series).
|
||||
* FEATURE: vmagent: expose `promscrape_series_limit_max_series` and `promscrape_series_limit_current_series` metrics at `http://vmagent:8429/metrics` for scrape targets with the [enabled series limiter](https://docs.victoriametrics.com/vmagent.html#cardinality-limiter).
|
||||
* FEATURE: vmagent: return error if `sample_limit` or `series_limit` options are set when [stream parsing mode](https://docs.victoriametrics.com/vmagent.html#stream-parsing-mode) is enabled, since these limits cannot be applied in stream parsing mode.
|
||||
* FEATURE: vmalert: add `-remoteRead.disablePathAppend` command-line flag, which allows specifying the full `-remoteRead.url`. If `-remoteRead.disablePathAppend` is set, then `vmalert` doesn't add `/api/v1/query` suffix to `-remoteRead.url`.
|
||||
* FEATURE: add trigonometric functions, which are going to be added in [Prometheus 2.31](https://github.com/prometheus/prometheus/pull/9239): [acosh](https://docs.victoriametrics.com/MetricsQL.html#acosh), [asinh](https://docs.victoriametrics.com/MetricsQL.html#asinh), [atan](https://docs.victoriametrics.com/MetricsQL.html#atan), [atanh](https://docs.victoriametrics.com/MetricsQL.html#atanh), [cosh](https://docs.victoriametrics.com/MetricsQL.html#cosh), [deg](https://docs.victoriametrics.com/MetricsQL.html#deg), [rad](https://docs.victoriametrics.com/MetricsQL.html#rad), [sinh](https://docs.victoriametrics.com/MetricsQL.html#sinh), [tan](https://docs.victoriametrics.com/MetricsQL.html#tan), [tanh](https://docs.victoriametrics.com/MetricsQL.html#tanh). Also add `atan2` binary operator. See [this pull request](https://github.com/prometheus/prometheus/pull/9248).
|
||||
* FEATURE: consistently return the same set of time series from [limitk](https://docs.victoriametrics.com/MetricsQL.html#limitk) function. This improves the usability of periodically refreshed graphs.
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): varios UX improvements. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1711).
|
||||
|
||||
* BUGFIX: vmstorage: fix `unaligned 64-bit atomic operation` panic on 32-bit architectures (arm and 386). The panic has been introduced in v1.67.0.
|
||||
* BUGFIX: vmalert, vmauth: prevent from frequent closing of TCP connections established to backends under high load. This should reduce the number of TCP sockets in `TIME_WAIT` state at `vmalert` and `vmauth` under high load. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/1704).
|
||||
* BUGFIX: vmagent: set `honor_timestamps: true` by default in [scrape configs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config) if this options isn't set explicitly. This aligns the behaviour with Prometheus.
|
||||
* BUGFIX: vmctl: fix importing boolean fields from InfluxDB line protocol. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1709).
|
||||
|
||||
|
||||
## [v1.67.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.67.0)
|
||||
|
|
|
@ -517,7 +517,7 @@ The shortlist of configuration flags is the following:
|
|||
-remoteRead.tlsServerName string
|
||||
Optional TLS server name to use for connections to -remoteRead.url. By default the server name from -remoteRead.url is used
|
||||
-remoteRead.url vmalert
|
||||
Optional URL to VictoriaMetrics or vmselect that will be used to restore alerts state. This configuration makes sense only if vmalert was configured with `remoteWrite.url` before and has been successfully persisted its state. E.g. http://127.0.0.1:8428
|
||||
Optional URL to VictoriaMetrics or vmselect that will be used to restore alerts state. This configuration makes sense only if vmalert was configured with `remoteWrite.url` before and has been successfully persisted its state. E.g. http://127.0.0.1:8428. See also -remoteRead.disablePathAppend
|
||||
-remoteWrite.basicAuth.password string
|
||||
Optional basic auth password for -remoteWrite.url
|
||||
-remoteWrite.basicAuth.passwordFile string
|
||||
|
|
Loading…
Reference in a new issue