@@ -38,14 +38,17 @@ const ChartWrapper = ({ data, options, chartId, onRegisterChart, onSyncHover })
3838
3939 const enhancedOptions = {
4040 ...options ,
41- onHover : ( event , activeElements ) => {
42- if ( activeElements . length > 0 ) {
43- const step = activeElements [ 0 ] . index ;
44- onSyncHover ( step , chartId ) ;
45- } else {
46- onSyncHover ( null , chartId ) ;
47- }
48- } ,
41+ onHover : ( event , activeElements ) => {
42+ if ( activeElements . length > 0 && chartRef . current ) {
43+ const { datasetIndex, index } = activeElements [ 0 ] ;
44+ const dataset = chartRef . current . data . datasets [ datasetIndex ] ;
45+ const point = dataset . data [ index ] ;
46+ const step = point . x ;
47+ onSyncHover ( step , chartId ) ;
48+ } else {
49+ onSyncHover ( null , chartId ) ;
50+ }
51+ } ,
4952 events : [ 'mousemove' , 'mouseout' , 'click' , 'touchstart' , 'touchmove' ] ,
5053 } ;
5154
@@ -85,8 +88,9 @@ export default function ChartContainer({
8588 } else if ( id !== sourceId ) {
8689 const activeElements = [ ] ;
8790 chart . data . datasets . forEach ( ( dataset , datasetIndex ) => {
88- if ( dataset . data && dataset . data . length > step ) {
89- activeElements . push ( { datasetIndex, index : step } ) ;
91+ const idx = dataset . data . findIndex ( p => p . x === step ) ;
92+ if ( idx !== - 1 ) {
93+ activeElements . push ( { datasetIndex, index : idx } ) ;
9094 }
9195 } ) ;
9296 chart . setActiveElements ( activeElements ) ;
@@ -96,47 +100,72 @@ export default function ChartContainer({
96100 } ) ;
97101 } , [ ] ) ;
98102
99- const parsedData = useMemo ( ( ) => {
100- const enabled = files . filter ( f => f . enabled !== false ) ;
101- return enabled . map ( file => {
102- if ( ! file . content ) return { ...file , metricsData : { } } ;
103- const lines = file . content . split ( '\n' ) ;
104- const metricsData = { } ;
103+ const parsedData = useMemo ( ( ) => {
104+ const enabled = files . filter ( f => f . enabled !== false ) ;
105+ return enabled . map ( file => {
106+ if ( ! file . content ) return { ...file , metricsData : { } } ;
107+ const lines = file . content . split ( '\n' ) ;
108+ const metricsData = { } ;
105109
106- const extractByKeyword = ( content , keyword ) => {
107- const results = [ ] ;
108- const numberRegex = / [ + - ] ? \d + (?: \. \d + ) ? (?: [ e E ] [ + - ] ? \d + ) ? / ;
109- content . split ( '\n' ) . forEach ( line => {
110- const idx = line . toLowerCase ( ) . indexOf ( keyword . toLowerCase ( ) ) ;
110+ const stepCfg = {
111+ enabled : file . config ?. useStepKeyword ,
112+ keyword : file . config ?. stepKeyword || 'step:'
113+ } ;
114+
115+ const extractStep = ( line ) => {
116+ if ( ! stepCfg . enabled ) return null ;
117+ const idx = line . toLowerCase ( ) . indexOf ( stepCfg . keyword . toLowerCase ( ) ) ;
111118 if ( idx !== - 1 ) {
112- const after = line . substring ( idx + keyword . length ) ;
113- const match = after . match ( numberRegex ) ;
119+ const after = line . substring ( idx + stepCfg . keyword . length ) ;
120+ const match = after . match ( / [ + - ] ? \d + / ) ;
114121 if ( match ) {
115- const v = parseFloat ( match [ 0 ] ) ;
116- if ( ! isNaN ( v ) ) results . push ( v ) ;
122+ const s = parseInt ( match [ 0 ] , 10 ) ;
123+ if ( ! isNaN ( s ) ) return s ;
117124 }
118125 }
119- } ) ;
120- return results ;
121- } ;
126+ return null ;
127+ } ;
122128
123- metrics . forEach ( metric => {
124- let values = [ ] ;
125- if ( metric . mode === 'keyword' ) {
126- values = extractByKeyword ( file . content , metric . keyword ) ;
127- } else if ( metric . regex ) {
128- const reg = new RegExp ( metric . regex ) ;
129- lines . forEach ( line => {
130- reg . lastIndex = 0 ;
131- const m = reg . exec ( line ) ;
132- if ( m && m [ 1 ] ) {
133- const v = parseFloat ( m [ 1 ] ) ;
134- if ( ! isNaN ( v ) ) values . push ( v ) ;
129+ const extractByKeyword = ( linesArr , keyword ) => {
130+ const results = [ ] ;
131+ const numberRegex = / [ + - ] ? \d + (?: \. \d + ) ? (?: [ e E ] [ + - ] ? \d + ) ? / ;
132+ linesArr . forEach ( line => {
133+ const idx = line . toLowerCase ( ) . indexOf ( keyword . toLowerCase ( ) ) ;
134+ if ( idx !== - 1 ) {
135+ const after = line . substring ( idx + keyword . length ) ;
136+ const match = after . match ( numberRegex ) ;
137+ if ( match ) {
138+ const v = parseFloat ( match [ 0 ] ) ;
139+ if ( ! isNaN ( v ) ) {
140+ const step = extractStep ( line ) ;
141+ results . push ( { x : step !== null ? step : results . length , y : v } ) ;
142+ }
143+ }
135144 }
136145 } ) ;
137- }
138- metricsData [ metric . name || metric . keyword ] = values . map ( ( v , i ) => ( { x : i , y : v } ) ) ;
139- } ) ;
146+ return results ;
147+ } ;
148+
149+ metrics . forEach ( metric => {
150+ let points = [ ] ;
151+ if ( metric . mode === 'keyword' ) {
152+ points = extractByKeyword ( lines , metric . keyword ) ;
153+ } else if ( metric . regex ) {
154+ const reg = new RegExp ( metric . regex ) ;
155+ lines . forEach ( line => {
156+ reg . lastIndex = 0 ;
157+ const m = reg . exec ( line ) ;
158+ if ( m && m [ 1 ] ) {
159+ const v = parseFloat ( m [ 1 ] ) ;
160+ if ( ! isNaN ( v ) ) {
161+ const step = extractStep ( line ) ;
162+ points . push ( { x : step !== null ? step : points . length , y : v } ) ;
163+ }
164+ }
165+ } ) ;
166+ }
167+ metricsData [ metric . name || metric . keyword ] = points ;
168+ } ) ;
140169
141170 const range = file . config ?. dataRange ;
142171 if ( range && ( range . start > 0 || range . end !== undefined ) ) {
@@ -147,7 +176,7 @@ export default function ChartContainer({
147176 const endIndex = Math . min ( data . length , end ) ;
148177 return data . slice ( start , endIndex ) ;
149178 } ;
150- const reindex = data => data . map ( ( p , idx ) => ( { x : idx , y : p . y } ) ) ;
179+ const reindex = data => stepCfg . enabled ? data : data . map ( ( p , idx ) => ( { x : idx , y : p . y } ) ) ;
151180 Object . keys ( metricsData ) . forEach ( k => {
152181 metricsData [ k ] = reindex ( applyRange ( metricsData [ k ] ) ) ;
153182 } ) ;
@@ -203,29 +232,31 @@ export default function ChartContainer({
203232 } ) ;
204233
205234 const getComparisonData = ( data1 , data2 , mode ) => {
206- const minLength = Math . min ( data1 . length , data2 . length ) ;
235+ const map2 = new Map ( data2 . map ( p => [ p . x , p . y ] ) ) ;
207236 const result = [ ] ;
208- for ( let i = 0 ; i < minLength ; i ++ ) {
209- const v1 = data1 [ i ] . y ;
210- const v2 = data2 [ i ] . y ;
211- let diff ;
212- switch ( mode ) {
213- case 'absolute' :
214- diff = Math . abs ( v2 - v1 ) ;
215- break ;
216- case 'relative-normal' :
217- diff = v1 !== 0 ? ( v2 - v1 ) / v1 : 0 ;
218- break ;
219- case 'relative' : {
220- const ad = Math . abs ( v2 - v1 ) ;
221- diff = v1 !== 0 ? ad / Math . abs ( v1 ) : 0 ;
222- break ;
237+ data1 . forEach ( p1 => {
238+ if ( map2 . has ( p1 . x ) ) {
239+ const v1 = p1 . y ;
240+ const v2 = map2 . get ( p1 . x ) ;
241+ let diff ;
242+ switch ( mode ) {
243+ case 'absolute' :
244+ diff = Math . abs ( v2 - v1 ) ;
245+ break ;
246+ case 'relative-normal' :
247+ diff = v1 !== 0 ? ( v2 - v1 ) / v1 : 0 ;
248+ break ;
249+ case 'relative' : {
250+ const ad = Math . abs ( v2 - v1 ) ;
251+ diff = v1 !== 0 ? ad / Math . abs ( v1 ) : 0 ;
252+ break ;
253+ }
254+ default :
255+ diff = v2 - v1 ;
223256 }
224- default :
225- diff = v2 - v1 ;
257+ result . push ( { x : p1 . x , y : diff } ) ;
226258 }
227- result . push ( { x : i , y : diff } ) ;
228- }
259+ } ) ;
229260 return result ;
230261 } ;
231262
0 commit comments