Skip to content

Commit 5b45b8b

Browse files
committed
Improvement - VueUiVerticalBar - Add support for negative values
1 parent 3f7b412 commit 5b45b8b

File tree

1 file changed

+50
-37
lines changed

1 file changed

+50
-37
lines changed

src/components/vue-ui-vertical-bar.vue

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ onMounted(() => {
106106
error({
107107
componentName: 'VueUiVerticalBar',
108108
type: 'dataset'
109-
})
109+
});
110110
}
111111
112112
barCount.value = props.dataset.flatMap(serie => {
@@ -189,15 +189,14 @@ const immutableDataset = computed(() => {
189189
.map((serie, i) => {
190190
const id = `vertical_parent_${i}_${uid.value}`;
191191
const hasChildren = !!serie.children && serie.children.length > 0;
192-
if (![null, undefined].includes(serie.value) && serie.value < 0) {
193-
console.warn(`VueUiVerticalBar is not designed to graph negative values. ${serie.name || 'serie at index' + i} has the following value: ${serie.value}`)
194-
}
192+
195193
return {
196194
...serie,
197195
id,
198196
shape: 'square',
199197
opacity: segregated.value.includes(id) ? 0.5 : 1,
200-
value: hasChildren ? serie.children.map(c => c.value || 0).reduce((a, b) => a + b, 0) : (serie.value || 0),
198+
value: hasChildren ? serie.children.map(c => c.value || 0).reduce((a, b) => a + b, 0) : (Math.abs(serie.value) || 0),
199+
sign: serie.value >= 0 ? 1 : -1,
201200
hasChildren,
202201
isChild: false,
203202
segregate: () => segregate(id),
@@ -206,16 +205,14 @@ const immutableDataset = computed(() => {
206205
children: !serie.children || !serie.children.length ? [] : serie.children
207206
.toSorted((a, b) => isSortDown.value ? b.value - a.value : a.value - b.value)
208207
.map((c, j) => {
209-
if (![null, undefined].includes(c.value) && c.value < 0) {
210-
console.warn(`VueUiVerticalBar is not designed to graph negative values. ${c.name + ' child serie' || 'child serie at index' + i + ', ' + j } has the following value: ${c.value}`)
211-
}
212208
return {
213209
...c,
214-
value: c.value || 0,
210+
value: Math.abs(c.value) || 0,
211+
sign: c.value >= 0 ? 1 : -1,
215212
isChild: true,
216213
parentId: id,
217214
parentName: serie.name,
218-
parentValue: serie.value,
215+
parentValue: serie.value || hasChildren ? serie.children.map(c => c.value || 0).reduce((a, b) => a + b, 0) : 0,
219216
id: `vertical_child_${i}_${j}_${uid.value}`,
220217
childIndex: j,
221218
color: convertColorToHex(c.color) || convertColorToHex(serie.color) || customPalette.value[i] || palette[i] || palette[i % palette.length]
@@ -302,18 +299,18 @@ const mutableDataset = computed(() => {
302299
});
303300
304301
const total = computed(() => {
305-
return mutableDataset.value.map(serie => serie.value).reduce((a,b) => a + b);
302+
return mutableDataset.value.map(serie => Math.abs(serie.value)).reduce((a,b) => a + b, 0);
306303
});
307304
308305
function calcProportionToTotal(val, formatted = false, rounding = 0) {
309306
if(formatted) {
310307
return dataLabel({
311-
v: val / total.value * 100,
308+
v: Math.abs(val) / total.value * 100,
312309
s: '%',
313310
r: rounding
314311
});
315312
}
316-
return val / total.value;
313+
return Math.abs(val) / total.value;
317314
}
318315
319316
const bars = computed(() => {
@@ -326,6 +323,10 @@ const bars = computed(() => {
326323
})
327324
})
328325
326+
const hasNegative = computed(() => {
327+
return bars.value.map(b => b.sign).includes(-1)
328+
});
329+
329330
const max = computed(() => {
330331
return Math.max(...mutableDataset.value.flatMap(serie => {
331332
if(serie.children && serie.children.length) {
@@ -338,7 +339,7 @@ const max = computed(() => {
338339
339340
function calcBarWidth(val) {
340341
const ratio = val / max.value;
341-
return drawableArea.value.width * ratio;
342+
return (drawableArea.value.width / (hasNegative.value ? 2 : 1)) * ratio;
342343
}
343344
344345
function calcDataLabelX(val) {
@@ -353,8 +354,9 @@ function getParentData(serie, index) {
353354
return {
354355
y: start + (height / 2) - (FINAL_CONFIG.value.style.chart.layout.bars.parentLabels.fontSize),
355356
name: parent.name,
356-
value: [undefined, NaN, null].includes(parent.value) ? '' : parent.value.toFixed(FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.roundingValue),
357-
percentageToTotal: isNaN(parent.value / total.value) ? '' : calcProportionToTotal(parent.value, true, FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.percentage.roundingPercentage)
357+
value: [undefined, NaN, null].includes(parent.value) ? '' : parent.sign === 1 ? parent.value : -parent.value,
358+
percentageToTotal: isNaN(parent.value / total.value) ? '' : calcProportionToTotal(parent.value, true, FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.percentage.roundingPercentage),
359+
sign: parent.sign
358360
}
359361
}
360362
@@ -403,10 +405,10 @@ function useTooltip(bar, seriesIndex) {
403405
if (FINAL_CONFIG.value.style.chart.tooltip.showValue) {
404406
html += `<div>${FINAL_CONFIG.value.translations.value}: <b>${applyDataLabel(
405407
FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.formatter,
406-
bar.value,
408+
bar.sign === 1 ? bar.value : -bar.value,
407409
dataLabel({
408410
p: FINAL_CONFIG.value.style.chart.tooltip.prefix,
409-
v: bar.value,
411+
v: bar.sign === 1 ? bar.value : -bar.value,
410412
s: FINAL_CONFIG.value.style.chart.tooltip.suffix,
411413
r: FINAL_CONFIG.value.style.chart.tooltip.roundingValue
412414
}),
@@ -416,13 +418,13 @@ function useTooltip(bar, seriesIndex) {
416418
417419
if(FINAL_CONFIG.value.style.chart.tooltip.showPercentage) {
418420
html += `<div>${FINAL_CONFIG.value.translations.percentageToTotal} : <b>${dataLabel({
419-
v: bar.value / total.value * 100,
421+
v: Math.abs(bar.value) / total.value * 100,
420422
s: '%',
421423
r: FINAL_CONFIG.value.style.chart.tooltip.roundingPercentage
422424
})}</b></div>`;
423425
if(bar.isChild) {
424426
html += `<div>${FINAL_CONFIG.value.translations.percentageToSerie}: <b>${dataLabel({
425-
v: bar.value / bar.parentValue * 100,
427+
v: Math.abs(bar.value) / Math.abs(bar.parentValue) * 100,
426428
s: '%',
427429
r: FINAL_CONFIG.value.style.chart.tooltip.roundingPercentage
428430
})}</b></div>`;
@@ -432,16 +434,16 @@ function useTooltip(bar, seriesIndex) {
432434
}
433435
}
434436
435-
function makeDataLabel(value, datapoint, seriesIndex) {
437+
function makeDataLabel(value, datapoint, seriesIndex, sign) {
436438
if (!FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.show) {
437439
return '';
438440
}
439441
const label = applyDataLabel(
440442
FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.formatter,
441-
value,
443+
sign === 1 ? value : -value,
442444
dataLabel({
443445
p: FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.prefix,
444-
v: value,
446+
v: sign === 1 ? value : -value,
445447
s: FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.suffix,
446448
r: FINAL_CONFIG.value.style.chart.layout.bars.dataLabels.value.roundingValue
447449
}),
@@ -469,8 +471,8 @@ const table = computed(() => {
469471
return {
470472
color: bar.color,
471473
parentName: bar.name,
472-
parentValue: bar.value,
473-
percentageToTotal: bar.value / total.value,
474+
parentValue: bar.sign === 1 ? bar.value : -bar.value,
475+
percentageToTotal: Math.abs(bar.value) / total.value,
474476
childName: "",
475477
childValue: "",
476478
childPercentageToParent: "",
@@ -484,9 +486,9 @@ const table = computed(() => {
484486
parentValue: bar.parentValue,
485487
percentageToTotal: bar.parentValue / total.value,
486488
childName: bar.name,
487-
childValue: bar.value,
488-
childPercentageToParent: bar.value / bar.parentValue,
489-
childPercentageToTotal: bar.value / total.value
489+
childValue: bar.sign === 1 ? bar.value : -bar.value,
490+
childPercentageToParent: Math.abs(bar.value) / Math.abs(bar.parentValue),
491+
childPercentageToTotal: Math.abs(bar.value) / total.value
490492
}
491493
}else{
492494
return {
@@ -495,9 +497,9 @@ const table = computed(() => {
495497
parentValue: "",
496498
percentageToTotal: "",
497499
childName: bar.name,
498-
childValue: bar.value,
499-
childPercentageToParent: bar.value / bar.parentValue,
500-
childPercentageToTotal: bar.value / total.value
500+
childValue: bar.sign === 1 ? bar.value : -bar.value,
501+
childPercentageToParent: Math.abs(bar.value) / Math.abs(bar.parentValue),
502+
childPercentageToTotal: Math.abs(bar.value) / total.value
501503
}
502504
}
503505
}
@@ -674,7 +676,7 @@ defineExpose({
674676
<!-- UNDERLAYER -->
675677
<rect
676678
:data-cy="`vertical-bar-rect-underlayer-${i}`"
677-
:x="drawableArea.left"
679+
:x="hasNegative ? drawableArea.left + (drawableArea.width / 2) - (serie.sign === 1 ? 0 : calcBarWidth(serie.value) <= 0 ? 0.0001 : calcBarWidth(serie.value)) : drawableArea.left"
678680
:y="drawableArea.top + ((barGap + barHeight) * i)"
679681
:width="calcBarWidth(serie.value) <= 0 ? 0.0001 : calcBarWidth(serie.value)"
680682
:height="barHeight <= 0 ? 0.0001 : barHeight"
@@ -686,7 +688,7 @@ defineExpose({
686688
<g v-for="(serie, i) in bars">
687689
<!-- BARS -->
688690
<rect
689-
:x="drawableArea.left"
691+
:x="hasNegative ? drawableArea.left + (drawableArea.width / 2) - (serie.sign === 1 ? 0 : calcBarWidth(serie.value) <= 0 ? 0.0001 : calcBarWidth(serie.value)) : drawableArea.left"
690692
:y="drawableArea.top + ((barGap + barHeight) * i)"
691693
:width="calcBarWidth(serie.value) <= 0 ? 0.0001 : calcBarWidth(serie.value)"
692694
:height="barHeight <= 0 ? 0.0001 : barHeight"
@@ -707,19 +709,30 @@ defineExpose({
707709
:stroke="FINAL_CONFIG.style.chart.layout.separators.color"
708710
:stroke-width="FINAL_CONFIG.style.chart.layout.separators.strokeWidth"
709711
stroke-linecap="round"
712+
/>
713+
714+
<line
715+
v-if="hasNegative && FINAL_CONFIG.style.chart.layout.separators.show"
716+
:x1="drawableArea.left + drawableArea.width / 2"
717+
:x2="drawableArea.left + drawableArea.width / 2"
718+
:y1="drawableArea.top"
719+
:y2="drawableArea.bottom"
720+
:stroke="FINAL_CONFIG.style.chart.layout.separators.color"
721+
:stroke-width="FINAL_CONFIG.style.chart.layout.separators.strokeWidth"
722+
stroke-linecap="round"
710723
/>
711724

712725
<!-- DATALABELS -->
713726
<text
714727
:data-cy="`vertical-bar-datalabel-${i}`"
715-
:x="calcDataLabelX(serie.value) + 3 + FINAL_CONFIG.style.chart.layout.bars.dataLabels.offsetX"
728+
:x="!hasNegative ? calcDataLabelX(serie.value) + 3 + FINAL_CONFIG.style.chart.layout.bars.dataLabels.offsetX : (drawableArea.left + (drawableArea.width / 2) + (serie.sign === 1 ? -12: 12) + (serie.sign === 1 ? -FINAL_CONFIG.style.chart.layout.bars.dataLabels.offsetX : FINAL_CONFIG.style.chart.layout.bars.dataLabels.offsetX))"
716729
:y="drawableArea.top + ((barGap + barHeight) * i) + (barHeight / 2) + FINAL_CONFIG.style.chart.layout.bars.dataLabels.fontSize / 2"
717-
text-anchor="start"
730+
:text-anchor="!hasNegative || serie.sign === - 1 ? 'start' : 'end'"
718731
:font-size="FINAL_CONFIG.style.chart.layout.bars.dataLabels.fontSize"
719732
:fill="FINAL_CONFIG.style.chart.layout.bars.dataLabels.color"
720733
:font-weight="FINAL_CONFIG.style.chart.layout.bars.dataLabels.bold ? 'bold' : 'normal'"
721734
>
722-
{{ makeDataLabel(serie.value, serie, i) }}
735+
{{ makeDataLabel(serie.value, serie, i, serie.sign) }}
723736
</text>
724737

725738
<!-- CHILDREN | LONELY PARENTS NAMES -->
@@ -756,7 +769,7 @@ defineExpose({
756769
:font-weight="FINAL_CONFIG.style.chart.layout.bars.dataLabels.bold ? 'bold' : 'normal'"
757770
text-anchor="start"
758771
>
759-
{{ makeDataLabel(getParentData(serie, i).value), getParentData(serie, i), i }}
772+
{{ makeDataLabel(getParentData(serie, i).value), getParentData(serie, i), i, getParentData(serie, i).sign }}
760773
</text>
761774

762775
<!-- TOOLTIP TRAPS -->

0 commit comments

Comments
 (0)