@eturnity/eturnity_reusable_components 8.16.9-qa-16-03-26.0 → 8.16.9-qa-16-03-26.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eturnity/eturnity_reusable_components",
3
- "version": "8.16.9-qa-16-03-26.0",
3
+ "version": "8.16.9-qa-16-03-26.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -0,0 +1,234 @@
1
+ <template>
2
+ <div
3
+ style="
4
+ margin-top: 100px;
5
+ margin-left: 80px;
6
+ padding-bottom: 100px;
7
+ display: flex;
8
+ flex-direction: column;
9
+ "
10
+ >
11
+ <!-- Simple bar chart -->
12
+ <BarChart
13
+ :data="monthlyData"
14
+ width="700px"
15
+ height="400px"
16
+ :valueFormatter="valueFormatter"
17
+ :barWidth="60"
18
+ :isScrollable="false"
19
+ chartControlsPosition="bottom"
20
+ :isBottomFieldsShown="true"
21
+ :isSelectionEnabled="true"
22
+ :selectionSize="3"
23
+ @selection-change="handleSelectionChange"
24
+ >
25
+ </BarChart>
26
+ <br />
27
+ <br />
28
+
29
+ <!-- Stacked bar chart -->
30
+ <BarChart
31
+ yAxisTitle="Energy (kWh)"
32
+ :data="monthLabels"
33
+ :series="tariffZones"
34
+ width="700px"
35
+ height="400px"
36
+ :barWidth="60"
37
+ :valueFormatter="valueFormatter"
38
+ :legendsItemPerRow="4"
39
+ :splitButtonOptions="options"
40
+ :selectedSplitButton="selectedTimeFrame"
41
+ :showPercentageOnTooltip="true"
42
+ :isBottomFieldsShown="true"
43
+ :isLegendShown="true"
44
+ @select-split-button="handleSelectSplitButton"
45
+ @input-blur="handleInputBlur"
46
+ >
47
+ </BarChart>
48
+
49
+ <!-- Stacked bar chart -->
50
+ <BarChart
51
+ :data="monthLabels"
52
+ :series="tariffZones"
53
+ width="700px"
54
+ height="400px"
55
+ :barWidth="60"
56
+ :valueFormatter="valueFormatter"
57
+ :legendsItemPerRow="4"
58
+ :isBottomFieldsShown="true"
59
+ :isLegendShown="false"
60
+ fieldMode="percentage"
61
+ @select-split-button="handleSelectSplitButton"
62
+ @input-blur="handleInputBlur"
63
+ >
64
+ <!-- <template #tooltip="{ item, segment }">
65
+ <div style="display: flex; flex-direction: column">
66
+ {{ $c.log(item, segment) }}
67
+ <div>{{ item.label }}</div>
68
+ <div>{{ item.segments[0].value }} kWh</div>
69
+ </div>
70
+ </template> -->
71
+ </BarChart>
72
+ </div>
73
+ </template>
74
+
75
+ <script setup>
76
+ import { ref } from 'vue'
77
+ import BarChart from '@/components/barchart/index.vue'
78
+
79
+ const options = [
80
+ { label: 'Day', value: 'day' },
81
+ { label: 'Month', value: 'month' },
82
+ { label: 'Year', value: 'year' },
83
+ ]
84
+
85
+ const selectedTimeFrame = ref('day')
86
+
87
+ const handleSelectSplitButton = (value) => {
88
+ selectedTimeFrame.value = value
89
+ }
90
+
91
+ const monthlyData = [
92
+ { label: 'Jan', value: 300 },
93
+ { label: 'Feb', value: 600 },
94
+ { label: 'Mar', value: 1000 },
95
+ { label: 'Apr', value: 1200 },
96
+ { label: 'May', value: 1400 },
97
+ { label: 'Jun', value: 1810 },
98
+ { label: 'Jul', value: 1400 },
99
+ { label: 'Aug', value: 1200 },
100
+ { label: 'Sep', value: 1000 },
101
+ // { label: 'Oct', value: 800 },
102
+ // { label: 'Nov', value: 600 },
103
+ // { label: 'Dec', value: 400 },
104
+ // { label: 'Jan', value: 300 },
105
+ // { label: 'Feb', value: 600 },
106
+ // { label: 'Mar', value: 1000 },
107
+ // { label: 'Apr', value: 1200 },
108
+ // { label: 'May', value: 1400 },
109
+ // { label: 'Jun', value: 1810 },
110
+ // { label: 'Jul', value: 1400 },
111
+ // { label: 'Aug', value: 1200 },
112
+ // { label: 'Sep', value: 1000 },
113
+ // { label: 'Oct', value: 800 },
114
+ // { label: 'Nov', value: 600 },
115
+ // { label: 'Dec', value: 400 },
116
+
117
+ // ... more months
118
+ ]
119
+
120
+ const monthLabels = [
121
+ { label: 'Jan' },
122
+ { label: 'Feb' },
123
+ { label: 'Mar' },
124
+ { label: 'Apr' },
125
+ { label: 'May' },
126
+ { label: 'Jun' },
127
+ // ... more months
128
+ ]
129
+
130
+ const tariffZones = ref([
131
+ {
132
+ name: 'Tariff Zone 1',
133
+ data: [
134
+ { label: 'Jan', value: 200 },
135
+ { label: 'Feb', value: 130 },
136
+ { label: 'Mar', value: 220 },
137
+ { label: 'Apr', value: 230 },
138
+ { label: 'May', value: 200 },
139
+ { label: 'Jun', value: 210 },
140
+ // ... more months
141
+ ],
142
+ },
143
+ {
144
+ name: 'Tariff Zone 2',
145
+ data: [
146
+ { label: 'Jan', value: 200 },
147
+ { label: 'Feb', value: 100 },
148
+ { label: 'Mar', value: 270 },
149
+ { label: 'Apr', value: 180 },
150
+ { label: 'May', value: 300 },
151
+ { label: 'Jun', value: 250 },
152
+ // ... more months
153
+ ],
154
+ },
155
+ {
156
+ name: 'Tariff Zone 3',
157
+ data: [
158
+ { label: 'Jan', value: 200 },
159
+ { label: 'Feb', value: 100 },
160
+ { label: 'Mar', value: 210 },
161
+ { label: 'Apr', value: 220 },
162
+ { label: 'May', value: 300 },
163
+ { label: 'Jun', value: 190 },
164
+ // ... more months
165
+ ],
166
+ },
167
+ {
168
+ name: 'Tariff Zone 4',
169
+ data: [
170
+ { label: 'Jan', value: 200 },
171
+ { label: 'Feb', value: 100 },
172
+ { label: 'Mar', value: 210 },
173
+ { label: 'Apr', value: 220 },
174
+ { label: 'May', value: 300 },
175
+ { label: 'Jun', value: 190 },
176
+ // ... more months
177
+ ],
178
+ },
179
+ {
180
+ name: 'Tariff Zone 5',
181
+ data: [
182
+ { label: 'Jan', value: 200 },
183
+ { label: 'Feb', value: 100 },
184
+ { label: 'Mar', value: 210 },
185
+ { label: 'Apr', value: 220 },
186
+ { label: 'May', value: 300 },
187
+ { label: 'Jun', value: 190 },
188
+ // ... more months
189
+ ],
190
+ },
191
+ {
192
+ name: 'Tariff Zone 6',
193
+ data: [
194
+ { label: 'Jan', value: 200 },
195
+ { label: 'Feb', value: 100 },
196
+ { label: 'Mar', value: 210 },
197
+ { label: 'Apr', value: 220 },
198
+ { label: 'May', value: 300 },
199
+ { label: 'Jun', value: 190 },
200
+ // ... more months
201
+ ],
202
+ },
203
+ // ... more tariff zones
204
+ ])
205
+
206
+ const valueFormatter = (value) => {
207
+ return `${value} kWh`
208
+ }
209
+
210
+ const handleSelectionChange = (selectedBars) => {
211
+ console.log('selectedBars', selectedBars)
212
+ }
213
+
214
+ const handleInputBlur = (payload) => {
215
+ console.log({ payload })
216
+ const newVal = [...tariffZones.value].map((zone) => {
217
+ if (zone.name === payload.seriesName) {
218
+ zone.data = zone.data.map((item) => {
219
+ if (item.label === payload.label) {
220
+ item.value = payload.value
221
+ }
222
+ return item
223
+ })
224
+ }
225
+ return zone
226
+ })
227
+
228
+ console.log({ newVal })
229
+
230
+ tariffZones.value = newVal
231
+ }
232
+ </script>
233
+
234
+ <style lang="scss" scoped></style>
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <Container :is-chart-controls-shown-in-bottom="isChartControlsShownInBottom">
3
3
  <LabelsColumn :width="yAxisWidth">
4
- <LabelRow v-for="series in props.series" :key="series.name">
4
+ <LabelRow v-for="series in seriesData" :key="series.name">
5
5
  {{ series.name }}
6
6
  </LabelRow>
7
- <TotalRow v-if="props.series.length && fieldMode === 'percentage'">
7
+ <TotalRow v-if="seriesData.length && fieldMode === 'percentage'">
8
8
  {{ $gettext ? $gettext('Total (%)') : 'Total (%)' }}
9
9
  </TotalRow>
10
- <TotalRow v-if="props.series.length">
10
+ <TotalRow v-if="seriesData.length">
11
11
  {{ $gettext ? $gettext('Total (kWh)') : 'Total (kWh)' }}
12
12
  </TotalRow>
13
13
  </LabelsColumn>
@@ -18,9 +18,9 @@
18
18
  >
19
19
  <FieldsWrapper>
20
20
  <!-- For stacked bar chart -->
21
- <template v-if="props.series.length">
21
+ <template v-if="seriesData.length">
22
22
  <InputRow
23
- v-for="series in props.series"
23
+ v-for="series in seriesData"
24
24
  :key="series.name"
25
25
  :data-series-name="series.name"
26
26
  >
@@ -32,7 +32,14 @@
32
32
  >
33
33
  <InputNumber
34
34
  :allow-negative="false"
35
+ :error-message="null"
35
36
  input-height="36px"
37
+ :is-border-error-only="true"
38
+ :is-error="
39
+ fieldMode === 'percentage'
40
+ ? calculatePercentageTotal(item.label) !== 100
41
+ : false
42
+ "
36
43
  :number-precision="0"
37
44
  :min-decimals="0"
38
45
  text-align="center"
@@ -61,6 +68,7 @@
61
68
  :unit-name="fieldMode === 'percentage' ? '%' : ''"
62
69
  :value="calculatePercentageTotal(item.label)"
63
70
  />
71
+ {{ $c.log(calculatePercentageTotal(item.label)) }}
64
72
  </InputGroup>
65
73
  </TotalInputRow>
66
74
 
@@ -72,12 +80,19 @@
72
80
  :key="index"
73
81
  >
74
82
  <InputNumber
83
+ error-message="The entered values don’t add up to 100% so we will automatically scale the total kWh"
75
84
  input-height="36px"
85
+ :is-border-error-only="true"
86
+ :is-error="
87
+ fieldMode === 'percentage'
88
+ ? calculatePercentageTotal(item.label) !== 100
89
+ : false
90
+ "
76
91
  :is-read-only="true"
77
92
  :number-precision="2"
78
93
  :min-decimals="0"
79
94
  text-align="center"
80
- :value="calculateTotal(item.label)"
95
+ :value="calculateTotalValue(item.label)"
81
96
  />
82
97
  </InputGroup>
83
98
  </TotalInputRow>
@@ -110,7 +125,8 @@
110
125
  </template>
111
126
 
112
127
  <script setup>
113
- import { ref } from 'vue'
128
+ import { ref, watch, onMounted } from 'vue'
129
+ import styled from 'vue3-styled-components'
114
130
  import InputNumber from '../inputs/inputNumber'
115
131
 
116
132
  import {
@@ -161,6 +177,43 @@
161
177
  },
162
178
  })
163
179
 
180
+ const seriesData = ref([])
181
+
182
+ onMounted(() => {
183
+ seriesData.value = props.series.map((item) => {
184
+ const data = item.data.map((d) => ({
185
+ label: d.label,
186
+ value: d.value,
187
+ originalValue: d.value,
188
+ }))
189
+
190
+ return {
191
+ name: item.name,
192
+ data,
193
+ }
194
+ })
195
+ })
196
+
197
+ watch(props.series, () => {
198
+ const currentSeriesData = [...seriesData.value]
199
+ const newSeriesData = []
200
+
201
+ props.series.forEach((item, itemIndex) => {
202
+ const data = item.data.map((d, dIndex) => ({
203
+ label: d.label,
204
+ value: d.value,
205
+ originalValue: currentSeriesData[itemIndex].data[dIndex].originalValue,
206
+ }))
207
+
208
+ newSeriesData.push({
209
+ name: item.name,
210
+ data,
211
+ })
212
+ })
213
+
214
+ seriesData.value = [...newSeriesData]
215
+ })
216
+
164
217
  const emit = defineEmits([
165
218
  'sync-scroll',
166
219
  'input-blur',
@@ -175,11 +228,13 @@
175
228
  emit('input-focus', { seriesName, label })
176
229
  }
177
230
 
178
- const calculateTotal = (label) => {
179
- return props.series.reduce((sum, series) => {
231
+ const calculateTotalValue = (label) => {
232
+ const total = seriesData.value.reduce((sum, series) => {
180
233
  const value = series.data.find((d) => d.label === label)?.value || 0
181
234
  return sum + value
182
235
  }, 0)
236
+
237
+ return Math.round(total)
183
238
  }
184
239
 
185
240
  const syncScroll = (scrollLeft) => {
@@ -190,30 +245,59 @@
190
245
  container.scrollLeft = scrollLeft
191
246
  }
192
247
  }
193
- const getDisplayValue = (seriesData, label) => {
248
+
249
+ const calculateTotalOriginalValue = (label) => {
250
+ return seriesData.value.reduce((sum, series) => {
251
+ const value =
252
+ series.data.find((d) => d.label === label)?.originalValue || 0
253
+ return sum + value
254
+ }, 0)
255
+ }
256
+
257
+ const getDisplayValue = (data, label) => {
194
258
  if (props.fieldMode === 'absolute') {
195
- return seriesData.find((d) => d.label === label)?.value || ''
259
+ return data.find((d) => d.label === label)?.value || ''
196
260
  }
197
261
 
198
- const value = seriesData.find((d) => d.label === label)?.value || 0
199
- const total = calculateTotal(label)
200
- return total ? Number(((value / total) * 100).toFixed(0)) : 0
262
+ const value = data.find((d) => d.label === label)?.value || 0
263
+
264
+ const total = seriesData.value.reduce((sum, series) => {
265
+ const value =
266
+ series.data.find((d) => d.label === label)?.originalValue || 0
267
+ return sum + value
268
+ }, 0)
269
+
270
+ return Math.round((value / total) * 100)
201
271
  }
202
272
 
203
273
  const calculatePercentageTotal = (label) => {
204
- return props.series.reduce((sum, series) => {
274
+ const originalTotal = seriesData.value.reduce((sum, series) => {
275
+ const originalValue =
276
+ series.data.find((d) => d.label === label)?.originalValue || 0
277
+ return sum + originalValue
278
+ }, 0)
279
+
280
+ const totalPercentage = seriesData.value.reduce((sum, series) => {
205
281
  const value = series.data.find((d) => d.label === label)?.value || 0
206
- const total = calculateTotal(label)
207
- const percentage = total ? Number(((value / total) * 100).toFixed(0)) : 0
282
+ const percentage = originalTotal
283
+ ? Number((value / originalTotal) * 100)
284
+ : 0
208
285
  return sum + percentage
209
286
  }, 0)
287
+
288
+ return Math.round(totalPercentage)
210
289
  }
211
290
 
212
291
  const handleInputBlur = (_value, seriesName, label) => {
213
292
  let value = Number(_value)
214
293
 
215
294
  if (props.fieldMode === 'percentage') {
216
- const total = calculateTotal(label)
295
+ const total = seriesData.value.reduce((sum, series) => {
296
+ const value =
297
+ series.data.find((d) => d.label === label)?.originalValue || 0
298
+ return sum + value
299
+ }, 0)
300
+
217
301
  value = (value / 100) * total
218
302
  }
219
303
 
@@ -18,6 +18,18 @@ export function useTooltip(chartId, normalizedData) {
18
18
  }
19
19
  if (isObjectEqual(item, tooltipData.value)) return
20
20
 
21
+ const totalValue = item.segments.reduce((acc, segment) => {
22
+ return acc + segment.value
23
+ }, 0)
24
+
25
+ const segments = item.segments.map((segment) => {
26
+ let valuePercentage = (segment.value / totalValue) * 100
27
+ segment.valuePercentage = Math.round(valuePercentage)
28
+
29
+ return segment
30
+ })
31
+ item.segments = segments
32
+
21
33
  tooltipData.value = { ...item }
22
34
 
23
35
  const targetElement = series.length
@@ -30,7 +30,7 @@
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>
@@ -128,7 +128,11 @@
128
128
  :gradient-to="segment.gradientTo"
129
129
  />
130
130
  <TooltipText>
131
- {{ handleValueFormatter(segment.value) }}
131
+ {{
132
+ fieldMode === 'absolute' && showPercentageOnTooltip
133
+ ? `${segment.valuePercentage}%`
134
+ : handleValueFormatter(segment.value)
135
+ }}
132
136
  </TooltipText>
133
137
  </TooltipRow>
134
138
  </template>
@@ -171,12 +175,13 @@
171
175
  </template>
172
176
 
173
177
  <script setup>
174
- import { useSlots, computed } from 'vue'
178
+ import { useSlots, computed, ref } from 'vue'
175
179
 
176
180
  import ChartControls from './ChartControls'
177
181
  import BottomFields from './BottomFields'
178
182
  import SelectionBox from './SelectionBox'
179
183
  import Spinner from '../spinner'
184
+ import { numberToString } from '../../helpers/numberConverter'
180
185
 
181
186
  import {
182
187
  useTooltip,
@@ -296,6 +301,10 @@
296
301
  type: Boolean,
297
302
  default: false,
298
303
  },
304
+ showPercentageOnTooltip: {
305
+ type: Boolean,
306
+ default: false,
307
+ },
299
308
  })
300
309
 
301
310
  const generateChartId = () =>
@@ -378,8 +387,20 @@
378
387
  }
379
388
 
380
389
  const handleValueFormatter = (value) => {
381
- return props.valueFormatter
382
- ? props.valueFormatter(Math.round(value))
383
- : value
390
+ value = numberToString({
391
+ value,
392
+ numberPrecision: 0,
393
+ minDecimals: 0,
394
+ })
395
+
396
+ return props.valueFormatter ? props.valueFormatter(value) : value
397
+ }
398
+
399
+ const getYAxisLabel = (label) => {
400
+ return numberToString({
401
+ value: label,
402
+ numberPrecision: 0,
403
+ minDecimals: 0,
404
+ })
384
405
  }
385
406
  </script>
@@ -28,7 +28,6 @@ export const TotalRow = styled(LabelRow)``
28
28
 
29
29
  export const FieldsContainer = styled.div`
30
30
  flex: 1;
31
- overflow-x: auto;
32
31
  scrollbar-width: none;
33
32
 
34
33
  &::-webkit-scrollbar {
@@ -63,4 +62,9 @@ export const InputGroup = styled('div', {
63
62
  props.barWidth}px;
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
+ }
66
70
  `
@@ -50,6 +50,7 @@
50
50
  :has-slot="hasSlot"
51
51
  :has-unit="unitName && !!unitName.length"
52
52
  :input-height="inputHeight"
53
+ :is-border-error-only="isBorderErrorOnly"
53
54
  :is-disabled="disabled"
54
55
  :is-error="isError"
55
56
  :is-interactive="isInteractive"
@@ -57,6 +58,7 @@
57
58
  :no-border="noBorder"
58
59
  :placeholder="displayedPlaceholder"
59
60
  :read-only="isReadOnly"
61
+ :readonly="isReadOnly"
60
62
  :show-arrow-controls="showArrowControls"
61
63
  :show-linear-unit-name="showLinearUnitName"
62
64
  :slot-size="slotSize"
@@ -80,7 +82,7 @@
80
82
  >{{ unitName }}</UnitContainer
81
83
  >
82
84
  <IconWrapper
83
- v-if="isError && !showLinearUnitName"
85
+ v-if="isError && !showLinearUnitName && !isBorderErrorOnly"
84
86
  :margin-right="showSelect ? selectWidth : 0"
85
87
  size="16px"
86
88
  >
@@ -134,7 +136,9 @@
134
136
  </ArrowButton>
135
137
  </ArrowControls>
136
138
  </InputWrapper>
137
- <ErrorMessage v-if="isError">{{ errorMessage }}</ErrorMessage>
139
+ <ErrorMessage v-if="isError && errorMessage">{{
140
+ errorMessage
141
+ }}</ErrorMessage>
138
142
  </Container>
139
143
  </template>
140
144
 
@@ -205,6 +209,7 @@
205
209
  colorMode: String,
206
210
  showArrowControls: Boolean,
207
211
  readOnly: Boolean,
212
+ isBorderErrorOnly: Boolean,
208
213
  }
209
214
 
210
215
  const Container = styled('div', inputProps)`
@@ -236,16 +241,17 @@
236
241
  showLinearUnitName,
237
242
  colorMode,
238
243
  showArrowControls,
244
+ isBorderErrorOnly,
239
245
  }) =>
240
246
  showArrowControls
241
247
  ? '40px'
242
248
  : colorMode === 'transparent'
243
249
  ? '0'
244
250
  : slotSize
245
- ? isError && !showLinearUnitName
251
+ ? isError && !showLinearUnitName && !isBorderErrorOnly
246
252
  ? 'calc(' + slotSize + ' + 24px)'
247
253
  : 'calc(' + slotSize + ' + 10px)'
248
- : isError && !showLinearUnitName
254
+ : isError && !showLinearUnitName && !isBorderErrorOnly
249
255
  ? '24px'
250
256
  : '5px'};
251
257
  border-radius: ${(props) =>
@@ -708,6 +714,10 @@
708
714
  type: Boolean,
709
715
  default: false,
710
716
  },
717
+ isBorderErrorOnly: {
718
+ type: Boolean,
719
+ default: false,
720
+ },
711
721
  },
712
722
  data() {
713
723
  return {