@eturnity/eturnity_reusable_components 8.19.8-EPDM-15273.1 → 8.19.8-EPDM-14690.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.vue +3 -1
- package/src/DemoChart.vue +424 -0
- package/src/TestChart.vue +229 -0
- package/src/assets/theme.js +78 -0
- package/src/components/barchart/BottomFields.vue +126 -36
- package/src/components/barchart/composables/useAxisCalculations.js +1 -1
- package/src/components/barchart/composables/useChartData.js +1 -1
- package/src/components/barchart/composables/useTooltip.js +28 -3
- package/src/components/barchart/index.vue +58 -15
- package/src/components/barchart/styles/bottomFields.js +18 -4
- package/src/components/barchart/styles/chart.js +1 -0
- package/src/components/buttons/mainButton/index.vue +2 -0
- package/src/components/errorMessage/index.vue +1 -1
- package/src/components/icon/index.vue +4 -2
- package/src/components/infoCard/index.vue +15 -3
- package/src/components/infoCard/infoCard.spec.js +3 -3
- package/src/components/infoText/index.vue +36 -31
- package/src/components/inputs/inputNumber/index.vue +92 -6
- package/src/components/pageTitle/index.vue +0 -1
- package/src/components/tag/proTag/index.vue +19 -0
- package/src/assets/svgIcons/flip_horizontally.svg +0 -6
package/src/assets/theme.js
CHANGED
@@ -416,6 +416,30 @@ const theme = (() => {
|
|
416
416
|
},
|
417
417
|
},
|
418
418
|
},
|
419
|
+
protag: {
|
420
|
+
main: {
|
421
|
+
default: {
|
422
|
+
backgroundColor: semanticColors.yellow[300],
|
423
|
+
textColor: semanticColors.teal[800],
|
424
|
+
borderColor: 'transparent',
|
425
|
+
},
|
426
|
+
hover: {
|
427
|
+
backgroundColor: semanticColors.yellow[300],
|
428
|
+
textColor: semanticColors.teal[800],
|
429
|
+
borderColor: '',
|
430
|
+
},
|
431
|
+
active: {
|
432
|
+
backgroundColor: semanticColors.yellow[300],
|
433
|
+
textColor: semanticColors.teal[800],
|
434
|
+
borderColor: '',
|
435
|
+
},
|
436
|
+
disabled: {
|
437
|
+
backgroundColor: semanticColors.yellow[300],
|
438
|
+
textColor: semanticColors.teal[400],
|
439
|
+
borderColor: '',
|
440
|
+
},
|
441
|
+
},
|
442
|
+
},
|
419
443
|
filter: {
|
420
444
|
main: {
|
421
445
|
default: {
|
@@ -590,6 +614,55 @@ const theme = (() => {
|
|
590
614
|
},
|
591
615
|
},
|
592
616
|
},
|
617
|
+
tertiary: {
|
618
|
+
// type
|
619
|
+
main: {
|
620
|
+
// variant: this is the default variant
|
621
|
+
default: {
|
622
|
+
backgroundColor: 'transparent',
|
623
|
+
textColor: semanticColors.purple[50],
|
624
|
+
borderColor: semanticColors.teal[500],
|
625
|
+
},
|
626
|
+
hover: {
|
627
|
+
backgroundColor: semanticColors.teal[600],
|
628
|
+
textColor: semanticColors.purple[50],
|
629
|
+
borderColor: semanticColors.teal[400],
|
630
|
+
},
|
631
|
+
active: {
|
632
|
+
backgroundColor: semanticColors.teal[700],
|
633
|
+
textColor: semanticColors.purple[50],
|
634
|
+
borderColor: semanticColors.teal[300],
|
635
|
+
},
|
636
|
+
disabled: {
|
637
|
+
textColor: semanticColors.grey[600],
|
638
|
+
backgroundColor: semanticColors.grey[500],
|
639
|
+
borderColor: semanticColors.grey[800],
|
640
|
+
},
|
641
|
+
},
|
642
|
+
cancel: {
|
643
|
+
// variant
|
644
|
+
default: {
|
645
|
+
backgroundColor: semanticColors.teal[200],
|
646
|
+
textColor: semanticColors.red[400],
|
647
|
+
borderColor: semanticColors.teal[500],
|
648
|
+
},
|
649
|
+
hover: {
|
650
|
+
backgroundColor: semanticColors.red[700],
|
651
|
+
textColor: semanticColors.red[700],
|
652
|
+
borderColor: semanticColors.teal[400],
|
653
|
+
},
|
654
|
+
active: {
|
655
|
+
backgroundColor: semanticColors.red[600],
|
656
|
+
textColor: semanticColors.red[200],
|
657
|
+
borderColor: semanticColors.teal[300],
|
658
|
+
},
|
659
|
+
disabled: {
|
660
|
+
textColor: semanticColors.grey[600],
|
661
|
+
backgroundColor: semanticColors.grey[500],
|
662
|
+
borderColor: semanticColors.grey[800],
|
663
|
+
},
|
664
|
+
},
|
665
|
+
},
|
593
666
|
ghost: {
|
594
667
|
// type
|
595
668
|
main: {
|
@@ -680,6 +753,11 @@ const theme = (() => {
|
|
680
753
|
fontSize: '14px',
|
681
754
|
iconWidth: '26px',
|
682
755
|
},
|
756
|
+
tiny: {
|
757
|
+
padding: '2px 5px',
|
758
|
+
fontSize: '10px',
|
759
|
+
iconWidth: '18px',
|
760
|
+
},
|
683
761
|
},
|
684
762
|
},
|
685
763
|
}
|
@@ -1,14 +1,14 @@
|
|
1
1
|
<template>
|
2
2
|
<Container :is-chart-controls-shown-in-bottom="isChartControlsShownInBottom">
|
3
3
|
<LabelsColumn :width="yAxisWidth">
|
4
|
-
<LabelRow v-for="series in
|
4
|
+
<LabelRow v-for="series in seriesData" :key="series.name">
|
5
5
|
{{ series.name }}
|
6
6
|
</LabelRow>
|
7
|
-
<TotalRow v-if="
|
8
|
-
{{ $gettext ? $gettext('Total (%)
|
7
|
+
<TotalRow v-if="seriesData.length && fieldMode === 'percentage'">
|
8
|
+
{{ $gettext ? `${$gettext('Total')} (%)` : 'Total (%)' }}
|
9
9
|
</TotalRow>
|
10
|
-
<TotalRow v-if="
|
11
|
-
{{ $gettext ? $gettext('Total (kWh)
|
10
|
+
<TotalRow v-if="seriesData.length">
|
11
|
+
{{ $gettext ? `${$gettext('Total')} (kWh)` : 'Total (kWh)' }}
|
12
12
|
</TotalRow>
|
13
13
|
</LabelsColumn>
|
14
14
|
|
@@ -18,27 +18,36 @@
|
|
18
18
|
>
|
19
19
|
<FieldsWrapper>
|
20
20
|
<!-- For stacked bar chart -->
|
21
|
-
<template v-if="
|
21
|
+
<template v-if="seriesData.length">
|
22
22
|
<InputRow
|
23
|
-
v-for="series in
|
23
|
+
v-for="series in seriesData"
|
24
24
|
:key="series.name"
|
25
25
|
:data-series-name="series.name"
|
26
26
|
>
|
27
27
|
<InputGroup
|
28
28
|
v-for="(item, index) in props.data"
|
29
|
+
:key="index"
|
29
30
|
:bar-width="barWidth"
|
30
31
|
:is-scrollable="isScrollable"
|
31
|
-
:key="index"
|
32
32
|
>
|
33
33
|
<InputNumber
|
34
34
|
:allow-negative="false"
|
35
|
+
:disabled="isInputsDisabled"
|
35
36
|
input-height="36px"
|
36
|
-
:
|
37
|
+
:is-disabled-styled-only="true"
|
38
|
+
:is-info-border="
|
39
|
+
fieldMode === 'percentage'
|
40
|
+
? calculatePercentageTotal(item.label) !== 100
|
41
|
+
: false
|
42
|
+
"
|
37
43
|
:min-decimals="0"
|
44
|
+
:number-precision="fieldMode === 'percentage' ? 2 : 0"
|
38
45
|
text-align="center"
|
39
46
|
:unit-name="fieldMode === 'percentage' ? '%' : ''"
|
40
47
|
:value="getDisplayValue(series.data, item.label)"
|
41
|
-
@input-blur="
|
48
|
+
@input-blur="
|
49
|
+
handleInputBlur($event, series.name, item.label, series.data)
|
50
|
+
"
|
42
51
|
@input-focus="handleInputFocus(series.name, item.label)"
|
43
52
|
/>
|
44
53
|
</InputGroup>
|
@@ -47,16 +56,18 @@
|
|
47
56
|
<TotalInputRow v-if="fieldMode === 'percentage'">
|
48
57
|
<InputGroup
|
49
58
|
v-for="(item, index) in props.data"
|
59
|
+
:key="index"
|
50
60
|
:bar-width="barWidth"
|
51
61
|
:is-scrollable="isScrollable"
|
52
|
-
:key="index"
|
53
62
|
>
|
54
63
|
<InputNumber
|
55
64
|
:allow-negative="false"
|
65
|
+
:disabled="isInputsDisabled"
|
56
66
|
input-height="36px"
|
67
|
+
:is-disabled-styled-only="true"
|
57
68
|
:is-read-only="true"
|
58
|
-
:number-precision="0"
|
59
69
|
:min-decimals="0"
|
70
|
+
:number-precision="fieldMode === 'percentage' ? 2 : 0"
|
60
71
|
text-align="center"
|
61
72
|
:unit-name="fieldMode === 'percentage' ? '%' : ''"
|
62
73
|
:value="calculatePercentageTotal(item.label)"
|
@@ -67,17 +78,23 @@
|
|
67
78
|
<TotalInputRow>
|
68
79
|
<InputGroup
|
69
80
|
v-for="(item, index) in props.data"
|
81
|
+
:key="index"
|
70
82
|
:bar-width="barWidth"
|
71
83
|
:is-scrollable="isScrollable"
|
72
|
-
:key="index"
|
73
84
|
>
|
74
85
|
<InputNumber
|
75
86
|
input-height="36px"
|
87
|
+
:is-border-error-only="true"
|
88
|
+
:is-info-border="
|
89
|
+
fieldMode === 'percentage'
|
90
|
+
? calculatePercentageTotal(item.label) !== 100
|
91
|
+
: false
|
92
|
+
"
|
76
93
|
:is-read-only="true"
|
77
|
-
:number-precision="2"
|
78
94
|
:min-decimals="0"
|
95
|
+
:number-precision="0"
|
79
96
|
text-align="center"
|
80
|
-
:value="
|
97
|
+
:value="calculateTotalValue(item.label)"
|
81
98
|
/>
|
82
99
|
</InputGroup>
|
83
100
|
</TotalInputRow>
|
@@ -88,17 +105,20 @@
|
|
88
105
|
<InputRow>
|
89
106
|
<InputGroup
|
90
107
|
v-for="(item, index) in props.data"
|
108
|
+
:key="index"
|
91
109
|
:bar-width="barWidth"
|
92
110
|
:is-scrollable="isScrollable"
|
93
|
-
:key="index"
|
94
111
|
>
|
95
112
|
<InputNumber
|
113
|
+
:allow-negative="false"
|
114
|
+
:disabled="isInputsDisabled"
|
96
115
|
input-height="36px"
|
116
|
+
:is-disabled-styled-only="true"
|
97
117
|
:min-decimals="0"
|
98
|
-
:number-precision="
|
118
|
+
:number-precision="0"
|
99
119
|
text-align="center"
|
100
120
|
:value="item.value"
|
101
|
-
@input-blur="handleInputBlur($event, null, item.label)"
|
121
|
+
@input-blur="handleInputBlur($event, null, item.label, null)"
|
102
122
|
@input-focus="handleInputFocus(null, item.label)"
|
103
123
|
/>
|
104
124
|
</InputGroup>
|
@@ -107,11 +127,22 @@
|
|
107
127
|
</FieldsWrapper>
|
108
128
|
</FieldsContainer>
|
109
129
|
</Container>
|
130
|
+
<InfoCardContainer
|
131
|
+
v-if="hasAnySegmentNotTotatTo100Percent && fieldMode === 'percentage'"
|
132
|
+
:yAxisWidth="yAxisWidth"
|
133
|
+
>
|
134
|
+
<InfoCard align-items="center" type="info">
|
135
|
+
<InfoCardBody>
|
136
|
+
{{ percentageErrorMessage }}
|
137
|
+
</InfoCardBody>
|
138
|
+
</InfoCard>
|
139
|
+
</InfoCardContainer>
|
110
140
|
</template>
|
111
141
|
|
112
142
|
<script setup>
|
113
|
-
import { ref } from 'vue'
|
143
|
+
import { ref, computed, watchEffect } from 'vue'
|
114
144
|
import InputNumber from '../inputs/inputNumber'
|
145
|
+
import InfoCard from '../infoCard'
|
115
146
|
|
116
147
|
import {
|
117
148
|
Container,
|
@@ -123,6 +154,8 @@
|
|
123
154
|
InputRow,
|
124
155
|
TotalInputRow,
|
125
156
|
InputGroup,
|
157
|
+
InfoCardContainer,
|
158
|
+
InfoCardBody,
|
126
159
|
} from './styles/bottomFields'
|
127
160
|
|
128
161
|
const props = defineProps({
|
@@ -159,6 +192,51 @@
|
|
159
192
|
default: 'absolute',
|
160
193
|
validator: (value) => ['absolute', 'percentage'].includes(value),
|
161
194
|
},
|
195
|
+
isInputsDisabled: {
|
196
|
+
type: Boolean,
|
197
|
+
default: false,
|
198
|
+
},
|
199
|
+
percentageErrorMessage: {
|
200
|
+
type: String,
|
201
|
+
default: '',
|
202
|
+
},
|
203
|
+
})
|
204
|
+
|
205
|
+
const seriesData = ref([])
|
206
|
+
|
207
|
+
watchEffect(() => {
|
208
|
+
let isNewSetOfSeries = false
|
209
|
+
const seriesDataCopy = [...seriesData.value]
|
210
|
+
if (
|
211
|
+
!seriesDataCopy.length ||
|
212
|
+
!props.series.length ||
|
213
|
+
seriesDataCopy.length !== props.series.length ||
|
214
|
+
!seriesDataCopy.some((item) => {
|
215
|
+
return props.series.map((s) => s.name).includes(item.name)
|
216
|
+
})
|
217
|
+
) {
|
218
|
+
isNewSetOfSeries = true
|
219
|
+
}
|
220
|
+
const currentSeriesData = !isNewSetOfSeries ? seriesDataCopy : []
|
221
|
+
const newSeriesData = []
|
222
|
+
|
223
|
+
props.series.forEach((item, itemIndex) => {
|
224
|
+
const data = item.data.map((d, dIndex) => ({
|
225
|
+
label: d.label,
|
226
|
+
value: d.value,
|
227
|
+
percentage: d.percentage,
|
228
|
+
originalValue: currentSeriesData.length
|
229
|
+
? currentSeriesData[itemIndex].data[dIndex].originalValue
|
230
|
+
: d.value,
|
231
|
+
}))
|
232
|
+
|
233
|
+
newSeriesData.push({
|
234
|
+
name: item.name,
|
235
|
+
data,
|
236
|
+
})
|
237
|
+
})
|
238
|
+
|
239
|
+
seriesData.value = [...newSeriesData]
|
162
240
|
})
|
163
241
|
|
164
242
|
const emit = defineEmits([
|
@@ -175,11 +253,13 @@
|
|
175
253
|
emit('input-focus', { seriesName, label })
|
176
254
|
}
|
177
255
|
|
178
|
-
const
|
179
|
-
|
256
|
+
const calculateTotalValue = (label) => {
|
257
|
+
const total = seriesData.value.reduce((sum, series) => {
|
180
258
|
const value = series.data.find((d) => d.label === label)?.value || 0
|
181
259
|
return sum + value
|
182
260
|
}, 0)
|
261
|
+
|
262
|
+
return Math.round(total)
|
183
263
|
}
|
184
264
|
|
185
265
|
const syncScroll = (scrollLeft) => {
|
@@ -190,34 +270,38 @@
|
|
190
270
|
container.scrollLeft = scrollLeft
|
191
271
|
}
|
192
272
|
}
|
193
|
-
|
273
|
+
|
274
|
+
const getDisplayValue = (data, label, shouldRound = true) => {
|
194
275
|
if (props.fieldMode === 'absolute') {
|
195
|
-
return
|
276
|
+
return data.find((d) => d.label === label)?.value
|
196
277
|
}
|
197
278
|
|
198
|
-
|
199
|
-
const total = calculateTotal(label)
|
200
|
-
return total ? Number(((value / total) * 100).toFixed(0)) : 0
|
279
|
+
return data.find((d) => d.label === label)?.percentage
|
201
280
|
}
|
202
281
|
|
203
282
|
const calculatePercentageTotal = (label) => {
|
204
|
-
|
205
|
-
const
|
206
|
-
|
207
|
-
const percentage = total ? Number(((value / total) * 100).toFixed(0)) : 0
|
283
|
+
const percentageTotal = seriesData.value.reduce((sum, series) => {
|
284
|
+
const percentage =
|
285
|
+
series.data.find((d) => d.label === label)?.percentage || 0
|
208
286
|
return sum + percentage
|
209
287
|
}, 0)
|
288
|
+
|
289
|
+
return Math.round(percentageTotal)
|
210
290
|
}
|
211
291
|
|
212
292
|
const handleInputBlur = (_value, seriesName, label) => {
|
213
|
-
|
214
|
-
|
215
|
-
if (props.fieldMode === 'percentage') {
|
216
|
-
const total = calculateTotal(label)
|
217
|
-
value = (value / 100) * total
|
293
|
+
if (props.isInputsDisabled) {
|
294
|
+
return
|
218
295
|
}
|
296
|
+
let value = Number(_value)
|
219
297
|
|
220
|
-
const payload = seriesName
|
298
|
+
const payload = seriesName
|
299
|
+
? {
|
300
|
+
seriesName,
|
301
|
+
label,
|
302
|
+
value,
|
303
|
+
}
|
304
|
+
: { label, value }
|
221
305
|
emit('input-blur', payload)
|
222
306
|
focusedInput.value = null
|
223
307
|
|
@@ -243,6 +327,12 @@
|
|
243
327
|
}
|
244
328
|
}
|
245
329
|
|
330
|
+
const hasAnySegmentNotTotatTo100Percent = computed(() => {
|
331
|
+
return props.data.some((d) => {
|
332
|
+
return calculatePercentageTotal(d.label) !== 100
|
333
|
+
})
|
334
|
+
})
|
335
|
+
|
246
336
|
const handleFieldsScroll = (event) => {
|
247
337
|
emit('sync-scroll', event.target.scrollLeft)
|
248
338
|
}
|
@@ -84,7 +84,7 @@ export function useAxisCalculations(props, maxValue) {
|
|
84
84
|
})
|
85
85
|
|
86
86
|
const yAxisWidth = computed(() => {
|
87
|
-
return !!props.yAxisTitle || props.isBottomFieldsShown ? '
|
87
|
+
return !!props.yAxisTitle || props.isBottomFieldsShown ? '80px' : '60px'
|
88
88
|
})
|
89
89
|
|
90
90
|
const isChartControlsShown = (position) => {
|
@@ -66,7 +66,7 @@ export function useChartData(props, paddedMaxValue) {
|
|
66
66
|
let accumulated = 0
|
67
67
|
return {
|
68
68
|
label: item.label,
|
69
|
-
segments: [...props.series].
|
69
|
+
segments: [...props.series].map((series, index) => {
|
70
70
|
const value =
|
71
71
|
series.data.find((d) => d.label === item.label)?.value || 0
|
72
72
|
accumulated += value
|
@@ -16,7 +16,21 @@ export function useTooltip(chartId, normalizedData) {
|
|
16
16
|
if (!showTooltipContent.value) {
|
17
17
|
showTooltipContent.value = true
|
18
18
|
}
|
19
|
-
if (isObjectEqual(item, tooltipData.value))
|
19
|
+
if (isObjectEqual(item, tooltipData.value)) {
|
20
|
+
return
|
21
|
+
}
|
22
|
+
|
23
|
+
const totalValue = item.segments.reduce((acc, segment) => {
|
24
|
+
return acc + segment.value
|
25
|
+
}, 0)
|
26
|
+
|
27
|
+
const segments = item.segments.map((segment) => {
|
28
|
+
let valuePercentage = (segment.value / totalValue) * 100
|
29
|
+
segment.valuePercentage = Math.round(valuePercentage)
|
30
|
+
|
31
|
+
return segment
|
32
|
+
})
|
33
|
+
item.segments = segments
|
20
34
|
|
21
35
|
tooltipData.value = { ...item }
|
22
36
|
|
@@ -41,16 +55,27 @@ export function useTooltip(chartId, normalizedData) {
|
|
41
55
|
|
42
56
|
isInputFocused.value = true
|
43
57
|
const barData = normalizedData.value.find((item) => item.label === label)
|
44
|
-
|
45
58
|
if (!barData) return
|
59
|
+
|
60
|
+
const totalValue = barData.segments.reduce((acc, segment) => {
|
61
|
+
return acc + segment.value
|
62
|
+
}, 0)
|
63
|
+
const segments = barData.segments.map((segment) => {
|
64
|
+
let valuePercentage = (segment.value / totalValue) * 100
|
65
|
+
segment.valuePercentage = Math.round(valuePercentage)
|
66
|
+
|
67
|
+
return segment
|
68
|
+
})
|
69
|
+
barData.segments = segments
|
46
70
|
focusedBarData.value = barData
|
71
|
+
|
47
72
|
const barElement = document.querySelector(
|
48
73
|
`.barchart-${chartId} .bar-group:nth-child(${
|
49
74
|
normalizedData.value.indexOf(barData) + 1
|
50
75
|
})`
|
51
76
|
)
|
52
|
-
|
53
77
|
if (!barElement) return
|
78
|
+
|
54
79
|
// Get the last bar segment, samee as hover behavior
|
55
80
|
const targetElement = barElement.querySelector('.bar-segment:last-child')
|
56
81
|
const rect = targetElement.getBoundingClientRect()
|
@@ -17,7 +17,7 @@
|
|
17
17
|
/>
|
18
18
|
</ChartControlsWrapper>
|
19
19
|
<GraphSection :height="height" :width="width">
|
20
|
-
<YAxis :
|
20
|
+
<YAxis :height="height" :width="yAxisWidth">
|
21
21
|
<YAxisTitleWrapper v-if="yAxisTitle" :height="yAxisHeight">
|
22
22
|
{{ yAxisTitle }}
|
23
23
|
</YAxisTitleWrapper>
|
@@ -30,15 +30,15 @@
|
|
30
30
|
)
|
31
31
|
"
|
32
32
|
>
|
33
|
-
<YAxisLabel>{{ label }}</YAxisLabel>
|
33
|
+
<YAxisLabel>{{ getYAxisLabel(label) }}</YAxisLabel>
|
34
34
|
<YAxisLine :y-axis-width="yAxisWidth" />
|
35
35
|
</YAxisRow>
|
36
36
|
</YAxis>
|
37
37
|
|
38
38
|
<ScrollContainer
|
39
39
|
:class="`chart-scroll-container-${chartId}`"
|
40
|
-
:is-scrollable="isScrollable"
|
41
40
|
:height="height"
|
41
|
+
:is-scrollable="isScrollable"
|
42
42
|
@scroll="handleChartScroll"
|
43
43
|
>
|
44
44
|
<ChartContent
|
@@ -64,19 +64,19 @@
|
|
64
64
|
<BarsContainer>
|
65
65
|
<BarGroup
|
66
66
|
v-for="(item, index) in normalizedData"
|
67
|
+
:key="index"
|
67
68
|
:bar-width="barWidth"
|
68
69
|
class="bar-group"
|
69
70
|
:is-scrollable="isScrollable"
|
70
|
-
:key="index"
|
71
71
|
>
|
72
72
|
<BarWrapper>
|
73
73
|
<BarSegment
|
74
74
|
v-for="(segment, segIndex) in item.segments"
|
75
|
+
:key="segIndex"
|
75
76
|
class="bar-segment"
|
76
77
|
:gradient-from="getSegmentGradient(index, segment).from"
|
77
78
|
:gradient-to="getSegmentGradient(index, segment).to"
|
78
79
|
:height="`${segment.percentage}%`"
|
79
|
-
:key="segIndex"
|
80
80
|
:z-index="item.segments.length - segIndex"
|
81
81
|
@mouseenter="showTooltip(item, $event, series)"
|
82
82
|
@mouseleave="hideTooltip"
|
@@ -96,7 +96,7 @@
|
|
96
96
|
:left="tooltipStyle.left"
|
97
97
|
:top="tooltipStyle.top"
|
98
98
|
>
|
99
|
-
<slot :item="tooltipData" name="tooltip"
|
99
|
+
<slot :item="tooltipData" name="tooltip"></slot>
|
100
100
|
<TooltipTextWrapper v-if="!slots.tooltip && tooltipData">
|
101
101
|
<template v-if="!series.length">
|
102
102
|
<TooltipText font-weight="500">{{ tooltipData.label }}</TooltipText>
|
@@ -128,7 +128,11 @@
|
|
128
128
|
:gradient-to="segment.gradientTo"
|
129
129
|
/>
|
130
130
|
<TooltipText>
|
131
|
-
{{
|
131
|
+
{{
|
132
|
+
fieldMode === 'absolute' && showPercentageOnTooltip
|
133
|
+
? `${segment.valuePercentage}%`
|
134
|
+
: handleValueFormatter(segment.value)
|
135
|
+
}}
|
132
136
|
</TooltipText>
|
133
137
|
</TooltipRow>
|
134
138
|
</template>
|
@@ -159,7 +163,9 @@
|
|
159
163
|
:data="data"
|
160
164
|
:field-mode="fieldMode"
|
161
165
|
:is-chart-controls-shown-in-bottom="isChartControlsShown('bottom')"
|
166
|
+
:is-inputs-disabled="isLoading"
|
162
167
|
:is-scrollable="isScrollable"
|
168
|
+
:percentage-error-message="percentageErrorMessage"
|
163
169
|
:series="series"
|
164
170
|
:y-axis-width="yAxisWidth"
|
165
171
|
@input-blur="handleInputBlur"
|
@@ -171,12 +177,13 @@
|
|
171
177
|
</template>
|
172
178
|
|
173
179
|
<script setup>
|
174
|
-
import { useSlots, computed } from 'vue'
|
180
|
+
import { useSlots, computed, ref } from 'vue'
|
175
181
|
|
176
182
|
import ChartControls from './ChartControls'
|
177
183
|
import BottomFields from './BottomFields'
|
178
184
|
import SelectionBox from './SelectionBox'
|
179
185
|
import Spinner from '../spinner'
|
186
|
+
import { numberToString } from '../../helpers/numberConverter'
|
180
187
|
|
181
188
|
import {
|
182
189
|
useTooltip,
|
@@ -246,10 +253,6 @@
|
|
246
253
|
type: String,
|
247
254
|
default: '',
|
248
255
|
},
|
249
|
-
valueFormatter: {
|
250
|
-
type: Function,
|
251
|
-
default: null,
|
252
|
-
},
|
253
256
|
isLegendShown: {
|
254
257
|
type: Boolean,
|
255
258
|
default: false,
|
@@ -296,6 +299,14 @@
|
|
296
299
|
type: Boolean,
|
297
300
|
default: false,
|
298
301
|
},
|
302
|
+
showPercentageOnTooltip: {
|
303
|
+
type: Boolean,
|
304
|
+
default: false,
|
305
|
+
},
|
306
|
+
percentageErrorMessage: {
|
307
|
+
type: String,
|
308
|
+
default: '',
|
309
|
+
},
|
299
310
|
})
|
300
311
|
|
301
312
|
const generateChartId = () =>
|
@@ -378,8 +389,40 @@
|
|
378
389
|
}
|
379
390
|
|
380
391
|
const handleValueFormatter = (value) => {
|
381
|
-
|
382
|
-
|
383
|
-
|
392
|
+
let formattedValue = value
|
393
|
+
if (value < 1000) {
|
394
|
+
formattedValue = numberToString({
|
395
|
+
value: formattedValue,
|
396
|
+
numberPrecision: 0,
|
397
|
+
minDecimals: 0,
|
398
|
+
})
|
399
|
+
} else if (value < 1000000) {
|
400
|
+
formattedValue = numberToString({
|
401
|
+
value: Number(formattedValue / 1000),
|
402
|
+
numberPrecision: 2,
|
403
|
+
minDecimals: 2,
|
404
|
+
})
|
405
|
+
} else {
|
406
|
+
formattedValue = numberToString({
|
407
|
+
value: Number(formattedValue / 1000000),
|
408
|
+
numberPrecision: 2,
|
409
|
+
minDecimals: 2,
|
410
|
+
})
|
411
|
+
}
|
412
|
+
if (value < 1000) {
|
413
|
+
return `${formattedValue} kWh`
|
414
|
+
} else if (value < 1000000) {
|
415
|
+
return `${formattedValue} MWh`
|
416
|
+
} else {
|
417
|
+
return `${formattedValue} GWh`
|
418
|
+
}
|
419
|
+
}
|
420
|
+
|
421
|
+
const getYAxisLabel = (label) => {
|
422
|
+
return numberToString({
|
423
|
+
value: label,
|
424
|
+
numberPrecision: 0,
|
425
|
+
minDecimals: 0,
|
426
|
+
})
|
384
427
|
}
|
385
428
|
</script>
|
@@ -17,18 +17,18 @@ export const LabelsColumn = styled('div', { width: String })`
|
|
17
17
|
|
18
18
|
export const LabelRow = styled.div`
|
19
19
|
height: 32px;
|
20
|
+
padding-top: 5px;
|
20
21
|
font-size: 12px;
|
21
22
|
font-weight: 500;
|
22
23
|
color: ${(props) => props.theme.semanticColors.teal[600]};
|
23
24
|
display: flex;
|
24
|
-
align-items:
|
25
|
+
align-items: center;
|
25
26
|
`
|
26
27
|
|
27
28
|
export const TotalRow = styled(LabelRow)``
|
28
29
|
|
29
30
|
export const FieldsContainer = styled.div`
|
30
31
|
flex: 1;
|
31
|
-
overflow-x: auto;
|
32
32
|
scrollbar-width: none;
|
33
33
|
|
34
34
|
&::-webkit-scrollbar {
|
@@ -59,8 +59,22 @@ export const InputGroup = styled('div', {
|
|
59
59
|
barWidth: Number,
|
60
60
|
isScrollable: Boolean,
|
61
61
|
})`
|
62
|
-
${(props) => (props.isScrollable ? 'min-width' : 'width')}
|
63
|
-
props.barWidth}px;
|
62
|
+
${(props) => (props.isScrollable ? 'min-width' : 'width')}: 70px;
|
64
63
|
display: flex;
|
65
64
|
justify-content: center;
|
65
|
+
position: relative;
|
66
|
+
|
67
|
+
input[readonly] {
|
68
|
+
border: 1px solid ${(props) => props.theme.colors.grey4} !important;
|
69
|
+
}
|
70
|
+
`
|
71
|
+
|
72
|
+
export const InfoCardContainer = styled('div', { yAxisWidth: String })`
|
73
|
+
margin-left: ${(props) => props.yAxisWidth};
|
74
|
+
padding: 12px;
|
75
|
+
margin-top: 12px;
|
76
|
+
`
|
77
|
+
|
78
|
+
export const InfoCardBody = styled.div`
|
79
|
+
padding: 8px 0;
|
66
80
|
`
|