1- import { Box } from "@mui/material" ;
1+ import { Box , debounce } from "@mui/material" ;
2+ import { EChartsOption , SeriesOption } from "echarts" ;
23import ReactECharts from "echarts-for-react" ;
34import { useEffect , useState } from "react" ;
45import { coerce } from "semver" ;
56import sort from "semver/functions/sort" ;
67import { useAdapterContext } from "../../../contexts/AdapterContext" ;
78import { getStatisticsHistory } from "../../../lib/ioBroker" ;
89
9- const chartDefaults = {
10+ const chartDefaults : EChartsOption = {
1011 title : {
1112 text : "Installed version history" ,
1213 } ,
@@ -40,7 +41,6 @@ const chartDefaults = {
4041 } ,
4142 {
4243 type : "inside" ,
43- realtime : true ,
4444 } ,
4545 ] ,
4646 xAxis : [
@@ -56,89 +56,111 @@ const chartDefaults = {
5656 series : [ ] ,
5757} ;
5858
59+ async function loadSeries (
60+ name : string ,
61+ start ?: Date ,
62+ end ?: Date ,
63+ existing ?: SeriesOption [ ] ,
64+ ) {
65+ const stats = await getStatisticsHistory ( name , start , end ) ;
66+ const versions = new Set < string > ( ) ;
67+ for ( const date of Object . keys ( stats . counts ) ) {
68+ Object . keys ( stats . counts [ date ] . versions )
69+ . map ( ( v ) => coerce ( v ) )
70+ . filter ( ( v ) => ! ! v )
71+ . forEach ( ( v ) => versions . add ( v ! . version ) ) ;
72+ }
73+
74+ const sortedVersions = Array . from ( versions ) ;
75+ sort ( sortedVersions ) ;
76+ const series = sortedVersions . map < SeriesOption > ( ( v ) => {
77+ const existingData = ( existing ?. find ( ( e ) => e . name === v ) ?. data ??
78+ [ ] ) as [ string , number ] [ ] ;
79+ const data = [ ...existingData ] ;
80+ Object . keys ( stats . counts )
81+ . map ( ( date ) => [ date , stats . counts [ date ] . versions [ v ] || 0 ] as const )
82+ . forEach ( ( [ date , value ] ) => {
83+ if (
84+ ! existingData . some (
85+ ( [ existingDate ] ) => existingDate === date ,
86+ )
87+ ) {
88+ data . push ( [ date , value ] ) ;
89+ }
90+ } ) ;
91+ data . sort ( ( a , b ) => a [ 0 ] . localeCompare ( b [ 0 ] ) ) ;
92+ return {
93+ name : v ,
94+ type : "line" ,
95+ stack : name ,
96+ showSymbol : false ,
97+ areaStyle : { } ,
98+ emphasis : {
99+ focus : "series" ,
100+ } ,
101+ data,
102+ } ;
103+ } ) ;
104+
105+ // the "total" series is used also for version markers
106+ series . push ( {
107+ name : "Total" ,
108+ type : "line" ,
109+ symbol : "circle" ,
110+ showSymbol : false ,
111+ lineStyle : { color : "black" , width : 1.5 } ,
112+ itemStyle : { color : "black" } ,
113+ data : Object . keys ( stats . counts ) . map ( ( date ) => [
114+ date ,
115+ stats . counts [ date ] . total ,
116+ ] ) ,
117+ markLine : {
118+ data : [
119+ // all stable version markers
120+ ...Object . keys ( stats . stable ) . map ( ( date ) => [
121+ {
122+ name : `Stable\n${ stats . stable [ date ] } ` ,
123+ xAxis : date ,
124+ yAxis : 0 ,
125+ } ,
126+ {
127+ name : "end" ,
128+ xAxis : date ,
129+ yAxis : "max" ,
130+ } ,
131+ ] ) ,
132+ // all latest that aren't in stable
133+ ...Object . keys ( stats . latest )
134+ . filter ( ( date ) => ! stats . stable [ date ] )
135+ . map ( ( date ) => [
136+ {
137+ name : `Latest\n${ stats . latest [ date ] } ` ,
138+ xAxis : date ,
139+ yAxis : 0 ,
140+ } ,
141+ {
142+ name : "end" ,
143+ xAxis : date ,
144+ yAxis : "max" ,
145+ } ,
146+ ] ) ,
147+ ] as SeriesOption [ "markLine" ] [ "data" ] ,
148+ } ,
149+ } ) ;
150+
151+ return series ;
152+ }
153+
59154export function VersionHistory ( ) {
60155 const { name } = useAdapterContext ( ) ;
61- const [ option , setOption ] = useState < any > ( ) ;
156+ const [ option , setOption ] = useState < EChartsOption > ( ) ;
62157 const [ showLoading , setShowLoading ] = useState ( true ) ;
63158
64159 useEffect ( ( ) => {
65160 setOption ( undefined ) ;
66161 setShowLoading ( true ) ;
67162 const loadHistory = async ( ) => {
68- const stats = await getStatisticsHistory ( name ) ;
69- const versions = new Set < string > ( ) ;
70- for ( const date of Object . keys ( stats . counts ) ) {
71- Object . keys ( stats . counts [ date ] . versions )
72- . map ( ( v ) => coerce ( v ) )
73- . filter ( ( v ) => ! ! v )
74- . forEach ( ( v ) => versions . add ( v ! . version ) ) ;
75- }
76-
77- const sortedVersions = Array . from ( versions ) ;
78- sort ( sortedVersions ) ;
79- const series : any [ ] = sortedVersions . map ( ( v ) => ( {
80- name : v ,
81- type : "line" ,
82- stack : name ,
83- areaStyle : { } ,
84- emphasis : {
85- focus : "series" ,
86- } ,
87- data : Object . keys ( stats . counts ) . map ( ( date ) => [
88- date ,
89- stats . counts [ date ] . versions [ v ] || 0 ,
90- ] ) ,
91- } ) ) ;
92-
93- // the "total" series is used also for version markers
94- series . push ( {
95- name : "Total" ,
96- type : "line" ,
97- symbol : "circle" ,
98- lineStyle : { color : "black" , width : 1.5 } ,
99- itemStyle : { color : "black" } ,
100- label : {
101- show : true ,
102- position : "top" ,
103- } ,
104- data : Object . keys ( stats . counts ) . map ( ( date ) => [
105- date ,
106- stats . counts [ date ] . total ,
107- ] ) ,
108- markLine : {
109- data : [
110- // all stable version markers
111- ...Object . keys ( stats . stable ) . map ( ( date ) => [
112- {
113- name : `Stable\n${ stats . stable [ date ] } ` ,
114- xAxis : date ,
115- yAxis : 0 ,
116- } ,
117- {
118- name : "end" ,
119- xAxis : date ,
120- yAxis : "max" ,
121- } ,
122- ] ) ,
123- // all latest that aren't in stable
124- ...Object . keys ( stats . latest )
125- . filter ( ( date ) => ! stats . stable [ date ] )
126- . map ( ( date ) => [
127- {
128- name : `Latest\n${ stats . latest [ date ] } ` ,
129- xAxis : date ,
130- yAxis : 0 ,
131- } ,
132- {
133- name : "end" ,
134- xAxis : date ,
135- yAxis : "max" ,
136- } ,
137- ] ) ,
138- ] ,
139- } ,
140- } ) ;
141-
163+ const series = await loadSeries ( name ) ;
142164 setShowLoading ( false ) ;
143165 setOption ( {
144166 ...chartDefaults ,
@@ -151,16 +173,44 @@ export function VersionHistory() {
151173 setOption ( undefined ) ;
152174 } ) ;
153175 } , [ name ] ) ;
176+
154177 if ( ! option && ! showLoading ) {
155178 return null ;
156179 }
180+
181+ async function onDataZoom ( event : any , chart : any ) {
182+ console . log ( "data zoom" , event , chart ) ;
183+ console . log ( "options" , chart . getOption ( ) ) ;
184+
185+ // according to https://github.com/apache/echarts/issues/17919#issuecomment-1316090464
186+ const extent = chart
187+ . getModel ( )
188+ . getComponent ( "xAxis" , 0 )
189+ . axis . scale . getExtent ( ) ;
190+ console . log ( "extent" , extent ) ;
191+
192+ const series = await loadSeries (
193+ name ,
194+ new Date ( extent [ 0 ] ) ,
195+ new Date ( extent [ 1 ] ) ,
196+ option ?. series as SeriesOption [ ] ,
197+ ) ;
198+ setOption ( {
199+ ...chartDefaults ,
200+ series,
201+ } ) ;
202+ }
203+
157204 return (
158205 < Box sx = { { marginTop : 2 } } >
159206 < ReactECharts
160207 style = { { height : "400px" } }
161208 loadingOption = { {
162209 type : "default" ,
163210 } }
211+ onEvents = { {
212+ datazoom : debounce ( onDataZoom , 250 ) ,
213+ } }
164214 showLoading = { showLoading }
165215 option = { option || { ...chartDefaults } }
166216 />
0 commit comments