1
- import React , { PureComponent } from 'react' ;
1
+ import { useConsolidatedRef } from '@ui5/webcomponents-react-base' ;
2
+ import bestContrast from 'get-best-contrast-color' ;
3
+ import React , { forwardRef , Ref , RefObject , useCallback , useEffect , useRef , useMemo } from 'react' ;
2
4
import { HorizontalBar } from 'react-chartjs-2' ;
3
- import { populateData } from '../../util/populateData' ;
4
- import { ChartInternalProps } from '../../interfaces/ChartInternalProps' ;
5
+ import { useTheme } from 'react-jss' ;
5
6
import { DEFAULT_OPTIONS } from '../../config' ;
6
- import { formatTooltipLabel , getTextWidth , mergeConfig } from '../../util/utils' ;
7
7
import { ChartBaseProps } from '../../interfaces/ChartBaseProps' ;
8
+ import { withChartContainer } from '../../internal/ChartContainer/withChartContainer' ;
8
9
import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps' ;
9
- import { LOG_LEVEL , Logger } from '@ui5/webcomponents-react-base ' ;
10
- import { withChartContainer } from '../ChartContainer/withChartContainer ' ;
10
+ import { useChartData } from '../../util/populateData ' ;
11
+ import { formatTooltipLabel , getTextWidth , useMergedConfig } from '../../util/utils ' ;
11
12
import { BarChartPlaceholder } from './Placeholder' ;
12
13
13
14
export interface BarChartPropTypes extends ChartBaseProps { }
14
15
15
- @withChartContainer
16
- export class BarChart extends PureComponent < BarChartPropTypes > {
17
- static defaultProps = {
18
- ...ChartBaseDefaultProps ,
19
- internalNoMerge : true
20
- } ;
21
-
22
- static LoadingPlaceholder = BarChartPlaceholder ;
23
-
24
- // private static checkIfDataLabelIsInScale(context) {
25
- // const chartElement = getCurrentChartElementFromContext(context);
26
- // const maxXAxis = chartElement._xScale.width + chartElement._xScale.left;
27
- // const chartWidth = chartElement._model.x;
28
- // return chartWidth / maxXAxis > 0.9;
29
- // }
30
-
31
- getAnchor = ( context ) => {
32
- const { valueAxisFormatter } = this . props ;
33
-
34
- try {
35
- const datasetMeta = context . chart . getDatasetMeta ( context . datasetIndex ) ;
36
- const dataSetLength = context . chart . data . datasets . length ;
37
- const xAxisId = datasetMeta . xAxisID ;
38
- const yAxisId = datasetMeta . yAxisID ;
39
-
40
- const xAxis = context . chart . scales [ xAxisId ] ;
41
- const yAxis = context . chart . scales [ yAxisId ] ;
42
- if ( xAxis . options . stacked ) {
43
- if ( ! yAxis . options . stacked ) {
44
- return 'end' ;
45
- }
46
- if ( dataSetLength - 1 === context . datasetIndex ) {
47
- // highest stack
48
- return 'end' ;
49
- } else {
50
- const chartElement = datasetMeta . data [ context . dataIndex ] ;
51
- const barWidth = Math . abs ( chartElement . _model . base - chartElement . _model . x ) ;
52
- const text = valueAxisFormatter ( context . dataset . data [ context . dataIndex ] ) ;
53
- const textWidth = getTextWidth ( text ) ;
54
- if ( barWidth < 1.5 * textWidth ) {
55
- // arbitrary estimate
56
- return 'start' ;
57
- }
58
-
59
- return 'center' ;
60
- }
61
- }
62
- } catch ( e ) {
63
- Logger . log ( LOG_LEVEL . WARNING , e . message ) ;
64
- }
65
- return 'end' ;
66
- } ;
67
-
68
- render ( ) {
16
+ const BarChart = withChartContainer (
17
+ forwardRef ( ( props : BarChartPropTypes , ref : Ref < any > ) => {
69
18
const {
70
19
labels,
71
20
datasets,
72
- theme,
73
21
options,
74
22
categoryAxisFormatter,
75
23
valueAxisFormatter,
76
24
getDatasetAtEvent,
77
25
getElementAtEvent,
78
- colors
79
- } = this . props as BarChartPropTypes & ChartInternalProps ;
26
+ colors,
27
+ width,
28
+ height,
29
+ noLegend
30
+ } = props as BarChartPropTypes ;
80
31
81
- const bar = populateData ( labels , datasets , colors , theme . theme ) ;
32
+ const theme : any = useTheme ( ) ;
33
+ const data = useChartData ( labels , datasets , colors , theme . theme ) ;
82
34
83
- const mergedOptions = mergeConfig (
84
- {
85
- layout : {
86
- padding : {
87
- right : 15
88
- }
89
- } ,
35
+ const chartRef = useConsolidatedRef < any > ( ref ) ;
36
+ const legendRef : RefObject < HTMLDivElement > = useRef ( ) ;
37
+
38
+ const handleLegendItemPress = useCallback (
39
+ ( e ) => {
40
+ const clickTarget = ( e . currentTarget as unknown ) as HTMLLIElement ;
41
+ const datasetIndex = parseInt ( clickTarget . dataset . datasetindex ) ;
42
+ const { chartInstance } = chartRef . current ;
43
+ const meta = chartInstance . getDatasetMeta ( datasetIndex ) ;
44
+ meta . hidden = meta . hidden === null ? ! chartInstance . data . datasets [ datasetIndex ] . hidden : null ;
45
+ chartInstance . update ( ) ;
46
+ clickTarget . style . textDecoration = meta . hidden ? 'line-through' : 'unset' ;
47
+ } ,
48
+ [ legendRef . current , chartRef . current ]
49
+ ) ;
50
+
51
+ useEffect ( ( ) => {
52
+ if ( noLegend ) {
53
+ legendRef . current . innerHTML = '' ;
54
+ } else {
55
+ legendRef . current . innerHTML = chartRef . current . chartInstance . generateLegend ( ) ;
56
+ legendRef . current . querySelectorAll ( 'li' ) . forEach ( ( legendItem ) => {
57
+ legendItem . addEventListener ( 'click' , handleLegendItemPress ) ;
58
+ } ) ;
59
+ }
60
+ } , [ chartRef . current , legendRef . current , noLegend ] ) ;
61
+
62
+ const barChartDefaultConfig = useMemo ( ( ) => {
63
+ return {
90
64
scales : {
91
65
xAxes : [
92
66
{
@@ -112,52 +86,55 @@ export class BarChart extends PureComponent<BarChartPropTypes> {
112
86
} ,
113
87
plugins : {
114
88
datalabels : {
115
- // display: (context) => {
116
- // const anchor = this.getAnchor(context);
117
- // if (anchor === 'start') {
118
- // // edge case
119
- // const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex);
120
- // const chartElement = datasetMeta.data[context.dataIndex];
121
- // const barWidth = Math.abs(chartElement._model.base - chartElement._model.x);
122
- // const text = valueAxisFormatter(context.dataset.data[context.dataIndex]);
123
- // const textWidth = getTextWidth(text);
124
- // if (barWidth < textWidth - 5) {
125
- // // arbitrary 5px tolerance
126
- // return false;
127
- // }
128
- // }
129
- // return true;
130
- // },
131
- anchor : this . getAnchor ,
132
- align : 'end' ,
133
- offset : 0 ,
89
+ anchor : 'end' ,
90
+ align : 'start' ,
91
+ clip : true ,
92
+ display : ( context ) => {
93
+ const datasetMeta = context . chart . getDatasetMeta ( context . datasetIndex ) ;
94
+ const dataMeta = datasetMeta . data [ context . dataIndex ] ;
95
+ const width = dataMeta . _view . x - dataMeta . _view . base ;
96
+ const formattedValue = valueAxisFormatter ( context . dataset . data [ context . dataIndex ] ) ;
97
+ const textWidth = getTextWidth ( formattedValue ) + 4 ; // offset
98
+ return width >= textWidth ;
99
+ } ,
134
100
formatter : valueAxisFormatter ,
135
101
color : ( context ) => {
136
- const anchor = this . getAnchor ( context ) ;
137
- if ( anchor === 'end' ) {
138
- return '#666' ;
139
- } else {
140
- return '#fff' ;
141
- }
102
+ const datasetMeta = context . chart . getDatasetMeta ( context . datasetIndex ) ;
103
+ const dataMeta = datasetMeta . data [ context . dataIndex ] ;
104
+ return bestContrast ( dataMeta . _view . backgroundColor , [
105
+ /* sapUiBaseText */ '#32363a' ,
106
+ /* sapUiContentContrastTextColor */ '#ffffff'
107
+ ] ) ;
142
108
}
143
109
}
144
110
}
145
- } ,
146
- options
147
- ) ;
111
+ } ;
112
+ } , [ valueAxisFormatter , categoryAxisFormatter ] ) ;
113
+
114
+ const mergedOptions = useMergedConfig ( barChartDefaultConfig , options ) ;
148
115
149
116
return (
150
- < HorizontalBar
151
- ref = { this . props . innerChartRef }
152
- data = { bar }
153
- height = { this . props . height }
154
- width = { this . props . width }
155
- options = { mergedOptions }
156
- // @ts -ignore
157
- getDatasetAtEvent = { getDatasetAtEvent }
158
- // @ts -ignore
159
- getElementAtEvent = { getElementAtEvent }
160
- />
117
+ < >
118
+ < HorizontalBar
119
+ ref = { chartRef }
120
+ data = { data }
121
+ height = { height }
122
+ width = { width }
123
+ options = { mergedOptions }
124
+ getDatasetAtEvent = { getDatasetAtEvent }
125
+ getElementAtEvent = { getElementAtEvent }
126
+ />
127
+ < div ref = { legendRef } className = "legend" />
128
+ </ >
161
129
) ;
162
- }
163
- }
130
+ } )
131
+ ) ;
132
+
133
+ // @ts -ignore
134
+ BarChart . LoadingPlaceholder = BarChartPlaceholder ;
135
+ BarChart . defaultProps = {
136
+ ...ChartBaseDefaultProps
137
+ } ;
138
+ BarChart . displayName = 'BarChart' ;
139
+
140
+ export { BarChart } ;
0 commit comments