@@ -8,6 +8,9 @@ var Registry = require('../../registry');
8
8
var Axes = require ( '../../plots/cartesian/axes' ) ;
9
9
var getAxisGroup = require ( '../../plots/cartesian/constraints' ) . getAxisGroup ;
10
10
var Sieve = require ( './sieve.js' ) ;
11
+ var TEXTPAD = require ( './constants' ) . TEXTPAD ;
12
+ var Drawing = require ( '../../components/drawing' ) ;
13
+ var d3 = require ( '@plotly/d3' ) ;
11
14
12
15
/*
13
16
* Bar chart stacking/grouping positioning and autoscaling calculations
@@ -567,10 +570,14 @@ function setBaseAndTop(sa, sieve) {
567
570
}
568
571
}
569
572
570
- fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , {
573
+ var extremesOpts = {
571
574
tozero : tozero ,
572
575
padded : true
573
- } ) ;
576
+ } ;
577
+
578
+ addTextPadding ( extremesOpts , fullTrace , sa ) ;
579
+
580
+ fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , extremesOpts ) ;
574
581
}
575
582
}
576
583
@@ -641,12 +648,17 @@ function stackBars(sa, sieve, opts) {
641
648
642
649
// if barnorm is set, let normalizeBars update the axis range
643
650
if ( ! opts . norm ) {
644
- fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , {
651
+ var extremesOpts = {
645
652
// N.B. we don't stack base with 'base',
646
653
// so set tozero:true always!
647
654
tozero : true ,
648
655
padded : true
649
- } ) ;
656
+ } ;
657
+
658
+ // Add text padding if needed
659
+ addTextPadding ( extremesOpts , fullTrace , sa ) ;
660
+
661
+ fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , extremesOpts ) ;
650
662
}
651
663
}
652
664
}
@@ -752,10 +764,13 @@ function normalizeBars(sa, sieve, opts) {
752
764
}
753
765
}
754
766
755
- fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , {
767
+ var extremesOpts = {
756
768
tozero : tozero ,
757
769
padded : padded
758
- } ) ;
770
+ } ;
771
+ addTextPadding ( extremesOpts , fullTrace , sa ) ;
772
+
773
+ fullTrace . _extremes [ sa . _id ] = Axes . findExtremes ( sa , pts , extremesOpts ) ;
759
774
}
760
775
}
761
776
@@ -864,6 +879,98 @@ function getAxisLetter(ax) {
864
879
return ax . _id . charAt ( 0 ) ;
865
880
}
866
881
882
+ function measureTextWidth ( txt , font ) {
883
+ var element = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'text' ) ;
884
+ var sel = d3 . select ( element ) ;
885
+ sel . text ( txt )
886
+ . attr ( 'x' , 0 )
887
+ . attr ( 'y' , 0 )
888
+ . attr ( 'data-unformatted' , txt )
889
+ . call ( Drawing . font , font ) ;
890
+
891
+ var width = Drawing . bBox ( sel . node ( ) ) . width ;
892
+
893
+ // Clean up the temporary element
894
+ element . remove ( ) ;
895
+
896
+ return width ;
897
+ }
898
+
899
+ function findExtremeIndices ( values ) {
900
+ var maxPositiveIdx = - 1 , maxNegativeIdx = - 1 ;
901
+ var maxPositiveVal = - Infinity , maxNegativeVal = Infinity ;
902
+
903
+ for ( var i = 0 ; i < values . length ; i ++ ) {
904
+ if ( values [ i ] > 0 && values [ i ] > maxPositiveVal ) {
905
+ maxPositiveVal = values [ i ] ;
906
+ maxPositiveIdx = i ;
907
+ }
908
+ if ( values [ i ] < 0 && values [ i ] < maxNegativeVal ) {
909
+ maxNegativeVal = values [ i ] ;
910
+ maxNegativeIdx = i ;
911
+ }
912
+ }
913
+ return { positive : maxPositiveIdx , negative : maxNegativeIdx } ;
914
+ }
915
+
916
+ function getMaxLineCount ( texts ) {
917
+ var maxLines = 1 ;
918
+ for ( var i = 0 ; i < texts . length ; i ++ ) {
919
+ if ( texts [ i ] ) {
920
+ var lineCount = ( texts [ i ] . toString ( ) . match ( / < b r > / gi) || [ ] ) . length + 1 ;
921
+ maxLines = Math . max ( maxLines , lineCount ) ;
922
+ }
923
+ }
924
+ return maxLines ;
925
+ }
926
+
927
+ function addTextPadding ( extremesOpts , trace , sa ) {
928
+ var textpos = trace . textposition ;
929
+ if ( ! trace . text || ! textpos ) return ;
930
+
931
+ var hasOutsideText = Array . isArray ( textpos ) ?
932
+ textpos . indexOf ( 'outside' ) !== - 1 : textpos === 'outside' ;
933
+ if ( ! hasOutsideText ) return ;
934
+
935
+ var font = trace . outsidetextfont || trace . textfont ;
936
+ var fontSize = font && font . size ;
937
+ if ( ! fontSize ) return ;
938
+
939
+ if ( Array . isArray ( fontSize ) ) {
940
+ fontSize = Math . max . apply ( Math , fontSize . filter ( function ( s ) { return typeof s === 'number' ; } ) ) ;
941
+ if ( ! fontSize ) return ;
942
+ }
943
+
944
+ var isHorizontal = trace . orientation === 'h' ;
945
+ var values = isHorizontal ? trace . x : trace . y ;
946
+ if ( ! values ) return ;
947
+
948
+ var extremes = findExtremeIndices ( values ) ;
949
+ var positivePadding = 0 , negativePadding = 0 ;
950
+
951
+ if ( isHorizontal ) {
952
+ if ( extremes . positive >= 0 && trace . text [ extremes . positive ] ) {
953
+ positivePadding = measureTextWidth ( trace . text [ extremes . positive ] , font ) + TEXTPAD ;
954
+ }
955
+ if ( extremes . negative >= 0 && trace . text [ extremes . negative ] ) {
956
+ negativePadding = measureTextWidth ( trace . text [ extremes . negative ] , font ) + TEXTPAD ;
957
+ }
958
+ } else {
959
+ // For vertical bars, calculate height based on text for each extreme bar
960
+ if ( extremes . positive >= 0 && trace . text [ extremes . positive ] ) {
961
+ var positiveTexts = [ trace . text [ extremes . positive ] ] ;
962
+ positivePadding = fontSize * getMaxLineCount ( positiveTexts ) + TEXTPAD ;
963
+ }
964
+ if ( extremes . negative >= 0 && trace . text [ extremes . negative ] ) {
965
+ var negativeTexts = [ trace . text [ extremes . negative ] ] ;
966
+ negativePadding = fontSize * getMaxLineCount ( negativeTexts ) + TEXTPAD ;
967
+ }
968
+ }
969
+
970
+ if ( extremes . positive >= 0 && positivePadding > 0 ) extremesOpts . ppadplus = positivePadding ;
971
+ if ( extremes . negative >= 0 && negativePadding > 0 ) extremesOpts . ppadminus = negativePadding ;
972
+ }
973
+
867
974
module . exports = {
868
975
crossTraceCalc : crossTraceCalc ,
869
976
setGroupPositions : setGroupPositions
0 commit comments