@@ -59,6 +59,7 @@ import {
59
59
} from './labelLayoutHelper' ;
60
60
import { labelInner , animateLabelValue } from './labelStyle' ;
61
61
import { normalizeRadian } from 'zrender/src/contain/util' ;
62
+ import { throttle } from '../util/throttle' ;
62
63
63
64
interface LabelDesc {
64
65
label : ZRText
@@ -194,10 +195,129 @@ function extendWithKeys(target: Dictionary<any>, source: Dictionary<any>, keys:
194
195
195
196
const LABEL_LAYOUT_PROPS = [ 'x' , 'y' , 'rotation' ] ;
196
197
198
+ /**
199
+ * Emphasis manager for handling label emphasis state changes
200
+ */
201
+ class EmphasisManager {
202
+ // eslint-disable-next-line no-undef
203
+ private currentEmphasisLabels : Set < Element > = new Set ( ) ;
204
+ private labelsNeedsHideOverlap : LabelLayoutWithGeometry [ ] = [ ] ;
205
+ // eslint-disable-next-line no-undef
206
+ private originalStates : Map < Element , boolean > = new Map ( ) ;
207
+
208
+ setLabelsNeedsHideOverlap ( labels : LabelLayoutWithGeometry [ ] ) : void {
209
+ this . clear ( ) ;
210
+ if ( labels . length === 0 ) {
211
+ return ;
212
+ }
213
+
214
+ this . labelsNeedsHideOverlap = labels ;
215
+
216
+ // Record original ignore states only when needed
217
+ labels . forEach ( item => {
218
+ this . originalStates . set ( item . label , item . label . ignore ) ;
219
+ if ( item . labelLine ) {
220
+ this . originalStates . set ( item . labelLine , item . labelLine . ignore ) ;
221
+ }
222
+ } ) ;
223
+ }
224
+
225
+ handleEmphasisChange ( targetLabel : Element , isEnteringEmphasis : boolean ) : void {
226
+ // Early return if no labels need hideOverlap processing
227
+ if ( this . labelsNeedsHideOverlap . length === 0 ) {
228
+ return ;
229
+ }
230
+
231
+ if ( isEnteringEmphasis ) {
232
+ this . currentEmphasisLabels . add ( targetLabel ) ;
233
+ }
234
+ else {
235
+ this . currentEmphasisLabels . delete ( targetLabel ) ;
236
+ }
237
+
238
+ if ( this . currentEmphasisLabels . size === 0 ) {
239
+ // No emphasis labels, restore original state
240
+ this . restoreOriginalState ( ) ;
241
+ }
242
+ else {
243
+ // Re-sort with emphasis labels first and call hideOverlap
244
+ this . reorderAndHideOverlap ( ) ;
245
+ }
246
+
247
+ // Note: api.updateLabelLayout() will be called externally
248
+ }
249
+
250
+ private reorderAndHideOverlap = throttle ( ( ) => {
251
+ if ( this . labelsNeedsHideOverlap . length === 0 ) {
252
+ return ;
253
+ }
254
+
255
+ // Create a copy for reordering
256
+ const reorderedLabels = [ ...this . labelsNeedsHideOverlap ] ;
257
+
258
+ // Sort: emphasis labels first, then by original priority
259
+ reorderedLabels . sort ( ( a , b ) => {
260
+ const aIsEmphasis = this . currentEmphasisLabels . has ( a . label ) ? 1 : 0 ;
261
+ const bIsEmphasis = this . currentEmphasisLabels . has ( b . label ) ? 1 : 0 ;
262
+
263
+ // Emphasis labels come first
264
+ if ( aIsEmphasis !== bIsEmphasis ) {
265
+ return bIsEmphasis - aIsEmphasis ;
266
+ }
267
+
268
+ // Then by original priority
269
+ return ( ( b . suggestIgnore ? 1 : 0 ) - ( a . suggestIgnore ? 1 : 0 ) )
270
+ || ( b . priority - a . priority ) ;
271
+ } ) ;
272
+
273
+ // First restore all to show state
274
+ reorderedLabels . forEach ( item => {
275
+ item . label . ignore = false ;
276
+ const emphasisState = item . label . ensureState ( 'emphasis' ) ;
277
+ emphasisState . ignore = false ;
278
+
279
+ if ( item . labelLine ) {
280
+ item . labelLine . ignore = false ;
281
+ const lineEmphasisState = item . labelLine . ensureState ( 'emphasis' ) ;
282
+ lineEmphasisState . ignore = false ;
283
+ }
284
+ } ) ;
285
+
286
+ // Call hideOverlap with isOrdered = true
287
+ hideOverlap ( reorderedLabels , true ) ;
288
+ } , 16 , true ) ;
289
+
290
+ private restoreOriginalState = throttle ( ( ) => {
291
+ this . labelsNeedsHideOverlap . forEach ( item => {
292
+ const originalIgnore = this . originalStates . get ( item . label ) ?? false ;
293
+ item . label . ignore = originalIgnore ;
294
+
295
+ // For emphasis state, use the original hideOverlap logic
296
+ const emphasisState = item . label . ensureState ( 'emphasis' ) ;
297
+ emphasisState . ignore = originalIgnore ;
298
+
299
+ if ( item . labelLine ) {
300
+ const originalLineIgnore = this . originalStates . get ( item . labelLine ) ?? false ;
301
+ item . labelLine . ignore = originalLineIgnore ;
302
+
303
+ const lineEmphasisState = item . labelLine . ensureState ( 'emphasis' ) ;
304
+ lineEmphasisState . ignore = originalLineIgnore ;
305
+ }
306
+ } ) ;
307
+ } , 16 , true ) ;
308
+
309
+ clear ( ) : void {
310
+ this . currentEmphasisLabels . clear ( ) ;
311
+ this . labelsNeedsHideOverlap = [ ] ;
312
+ this . originalStates . clear ( ) ;
313
+ }
314
+ }
315
+
197
316
class LabelManager {
198
317
199
318
private _labelList : LabelDesc [ ] = [ ] ;
200
319
private _chartViewList : ChartView [ ] = [ ] ;
320
+ private _emphasisManager : EmphasisManager = new EmphasisManager ( ) ;
201
321
202
322
constructor ( ) { }
203
323
@@ -323,6 +443,32 @@ class LabelManager {
323
443
// Can only attach the text on the element with dataIndex
324
444
if ( textEl && ! ( textEl as ECElement ) . disableLabelLayout ) {
325
445
this . _addLabel ( ecData . dataIndex , ecData . dataType , seriesModel , textEl , layoutOption ) ;
446
+ // Add emphasis state change listener for hideOverlap labels
447
+ const resolvedLayoutOption = isFunction ( layoutOption ) ? null : layoutOption ;
448
+ if ( resolvedLayoutOption && resolvedLayoutOption . hideOverlap ) {
449
+ const hostEl = child as ECElement ;
450
+ const originalOnHoverStateChange = hostEl . onHoverStateChange ;
451
+ const labelManager = this ;
452
+
453
+ hostEl . onHoverStateChange = function ( toState : string ) {
454
+ // Call original handler first
455
+ if ( originalOnHoverStateChange ) {
456
+ originalOnHoverStateChange . call ( this , toState ) ;
457
+ }
458
+
459
+ // Handle emphasis state change for hideOverlap labels
460
+ if ( toState === 'emphasis' || toState === 'normal' ) {
461
+ // Find the label element - could be textEl or child itself
462
+ const labelElement = textEl || this ;
463
+
464
+ // Use EmphasisManager to handle the state change
465
+ labelManager . _emphasisManager . handleEmphasisChange (
466
+ labelElement ,
467
+ toState === 'emphasis'
468
+ ) ;
469
+ }
470
+ } ;
471
+ }
326
472
}
327
473
} ) ;
328
474
}
@@ -466,6 +612,7 @@ class LabelManager {
466
612
467
613
restoreIgnore ( labelsNeedsHideOverlap ) ;
468
614
hideOverlap ( labelsNeedsHideOverlap ) ;
615
+ this . _emphasisManager . setLabelsNeedsHideOverlap ( labelsNeedsHideOverlap ) ;
469
616
}
470
617
471
618
/**
0 commit comments