Skip to content

Commit 41f07f3

Browse files
committed
Improved yLabels nice scales in VueUiXy, VueUiDonutEvolution & VueUiCandlestick
1 parent 94aa031 commit 41f07f3

File tree

8 files changed

+103
-63
lines changed

8 files changed

+103
-63
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vue-data-ui",
33
"private": false,
4-
"version": "1.9.66",
4+
"version": "1.9.67",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [

src/components/vue-ui-candlestick.vue

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup>
22
import { ref, computed, onMounted, nextTick } from "vue";
3-
import { canShowValue, closestDecimal, shiftHue, opacity, createUid, createCsvContent, downloadCsv } from "../lib";
3+
import { canShowValue, closestDecimal, shiftHue, opacity, createUid, createCsvContent, downloadCsv, calculateNiceScale } from "../lib";
44
import mainConfig from "../default_configs.json";
55
import pdf from "../pdf";
66
import img from "../img";
@@ -133,11 +133,15 @@ const extremes = computed(() => {
133133
}
134134
});
135135
136+
const niceScale = computed(() => {
137+
return calculateNiceScale(extremes.value.min, extremes.value.max, 10)
138+
})
139+
136140
function convertToPlot(item, index) {
137141
return {
138142
...item,
139143
x: drawingArea.value.left + (index * slot.value) + (slot.value / 2),
140-
y: drawingArea.value.top + (1 - (item / extremes.value.max)) * drawingArea.value.height,
144+
y: drawingArea.value.top + (1 - (item / niceScale.value.max)) * drawingArea.value.height,
141145
value: item
142146
}
143147
}
@@ -162,20 +166,16 @@ const drawableDataset = computed(() => {
162166
});
163167
164168
function ratioToMax(value) {
165-
return value / extremes.value.max;
169+
return value / niceScale.value.max;
166170
}
167171
168172
const yLabels = computed(() => {
169-
const positiveStep = closestDecimal(extremes.value.max / candlestickConfig.value.style.layout.grid.yAxis.dataLabels.steps);
170-
const steps = [];
171-
for(let i = candlestickConfig.value.style.layout.grid.yAxis.dataLabels.steps; i >= 0; i -= 1) {
172-
const value = positiveStep * i ;
173-
steps.push({
174-
y: drawingArea.value.bottom - (drawingArea.value.height * ratioToMax(positiveStep * i)),
175-
value,
176-
});
177-
}
178-
return steps;
173+
return niceScale.value.ticks.map(t => {
174+
return {
175+
y: drawingArea.value.bottom - (drawingArea.value.height * ratioToMax(t)),
176+
value: t
177+
}
178+
})
179179
});
180180
181181
const xLabels = computed(() => {
@@ -402,7 +402,7 @@ defineExpose({
402402
<g v-if="candlestickConfig.style.layout.grid.yAxis.dataLabels.show">
403403
<g v-for="(yLabel, i) in yLabels">
404404
<line
405-
v-if="yLabel.value >= 0 && yLabel.value <= extremes.max"
405+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
406406
:x1="drawingArea.left"
407407
:x2="drawingArea.left - 5"
408408
:y1="yLabel.y"
@@ -411,7 +411,7 @@ defineExpose({
411411
:stroke-width="candlestickConfig.style.layout.grid.strokeWidth"
412412
/>
413413
<text
414-
v-if="yLabel.value >= 0 && yLabel.value <= extremes.max"
414+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
415415
:x="drawingArea.left - 8 + candlestickConfig.style.layout.grid.yAxis.dataLabels.offsetX"
416416
:y="yLabel.y + candlestickConfig.style.layout.grid.yAxis.dataLabels.fontSize / 3"
417417
:font-size="candlestickConfig.style.layout.grid.yAxis.dataLabels.fontSize"

src/components/vue-ui-donut-evolution.vue

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup>
22
import { ref, computed, nextTick } from "vue";
3-
import { calcMarkerOffsetX, calcMarkerOffsetY, calcNutArrowPath, canShowValue, closestDecimal, makeDonut, palette, convertColorToHex, opacity, createUid, sumByAttribute, createCsvContent, downloadCsv } from '../lib';
3+
import { calcMarkerOffsetX, calcMarkerOffsetY, calcNutArrowPath, canShowValue, closestDecimal, makeDonut, palette, convertColorToHex, opacity, createUid, sumByAttribute, createCsvContent, downloadCsv, calculateNiceScale } from '../lib';
44
import pdf from "../pdf";
55
import img from "../img";
66
import mainConfig from "../default_configs.json";
@@ -115,12 +115,15 @@ const drawableDataset = computed(() => {
115115
x,
116116
})
117117
}
118-
const maxSubtotal = Math.max(...arr.map(a => a.subtotal));
118+
119+
const minSubtotal = 0;
120+
const maxSubtotal = Math.max(...arr.map(a => a.subtotal))
121+
119122
return arr.map((a, i) => {
120123
const radiusReference = (slit.value / 2) * 0.5;
121124
const radius = radiusReference > svg.value.width / 16 ? svg.value.width / 16 : radiusReference;
122125
const activeRadius = hoveredIndex.value === a.index ? svg.value.width / 20 : radius;
123-
const y = svg.value.absoluteHeight - padding.value.bottom - (svg.value.height * a.subtotal / maxSubtotal);
126+
const y = svg.value.absoluteHeight - padding.value.bottom - (svg.value.height * a.subtotal / calculateNiceScale(minSubtotal, maxSubtotal, 10).max);
124127
return {
125128
...a,
126129
y,
@@ -159,21 +162,21 @@ const extremes = computed(() => {
159162
}
160163
});
161164
165+
const niceScale = computed(() => {
166+
return calculateNiceScale(extremes.value.min, extremes.value.max, 10)
167+
})
168+
162169
function ratioToMax(value) {
163-
return value / extremes.value.max;
170+
return value / niceScale.value.max;
164171
}
165172
166173
const yLabels = computed(() => {
167-
const positiveStep = closestDecimal(extremes.value.max / donutEvolutionConfig.value.style.chart.layout.grid.yAxis.dataLabels.steps);
168-
const steps = [];
169-
for(let i = donutEvolutionConfig.value.style.chart.layout.grid.yAxis.dataLabels.steps; i >= 0; i -= 1) {
170-
const value = positiveStep * i ;
171-
steps.push({
172-
y: svg.value.absoluteHeight - padding.value.bottom - (svg.value.height * ratioToMax(positiveStep * i)),
173-
value,
174-
});
175-
}
176-
return steps;
174+
return niceScale.value.ticks.map(t => {
175+
return {
176+
y: svg.value.absoluteHeight - padding.value.bottom - (svg.value.height * ratioToMax(t)),
177+
value: t
178+
}
179+
})
177180
});
178181
179182
function displayArcPercentage(arc, stepBreakdown) {
@@ -452,7 +455,7 @@ defineExpose({
452455
<g v-if="donutEvolutionConfig.style.chart.layout.grid.yAxis.dataLabels.show" :class="{'donut-opacity': true, 'donut-behind': hoveredIndex !== null || isFixed}">
453456
<g v-for="(yLabel, i) in yLabels">
454457
<line
455-
v-if="yLabel.value >= 0 && yLabel.value <= extremes.max"
458+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
456459
:x1="padding.left"
457460
:x2="padding.left - 5"
458461
:y1="yLabel.y"
@@ -461,7 +464,7 @@ defineExpose({
461464
:stroke-width="donutEvolutionConfig.style.chart.layout.grid.strokeWidth"
462465
/>
463466
<text
464-
v-if="yLabel.value >= 0 && yLabel.value <= extremes.max"
467+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
465468
:x="padding.left - 8 + donutEvolutionConfig.style.chart.layout.grid.yAxis.dataLabels.offsetX"
466469
:y="yLabel.y + donutEvolutionConfig.style.chart.layout.grid.yAxis.dataLabels.fontSize / 3"
467470
:font-size="donutEvolutionConfig.style.chart.layout.grid.yAxis.dataLabels.fontSize"

src/components/vue-ui-xy.cy.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ describe('<VueUiXy />', () => {
9999
cy.get(`[data-cy="xy-line-tag-end-${i}"]`).should('exist')
100100
}
101101

102-
for(let i = 0; i < 11; i += 1) {
103-
cy.get(`[data-cy="xy-label-y-${i}"]`).should('exist');
104-
}
105-
106102
cy.get('[data-cy="xy-axis-xLabel"]').should('exist');
107103
cy.get('[data-cy="xy-axis-yLabel"]').should('exist');
108104

src/components/vue-ui-xy.vue

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@
454454
<g v-if="chartConfig.chart.grid.labels.show">
455455
<g v-for="(yLabel, i) in yLabels" :key="`yLabel_${i}`">
456456
<line
457-
v-if="yLabel.value >= min && yLabel.value <= max"
457+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
458458
:x1="drawingArea.left"
459459
:x2="drawingArea.left - 5"
460460
:y1="yLabel.y"
@@ -464,7 +464,7 @@
464464
/>
465465
<text
466466
:data-cy="`xy-label-y-${i}`"
467-
v-if="yLabel.value >= min && yLabel.value <= max"
467+
v-if="yLabel.value >= niceScale.min && yLabel.value <= niceScale.max"
468468
:x="drawingArea.left - 7"
469469
:y="yLabel.y + chartConfig.chart.labels.fontSize / 3"
470470
:font-size="chartConfig.chart.grid.labels.fontSize"
@@ -697,7 +697,8 @@ import {
697697
createUid,
698698
closestDecimal,
699699
createCsvContent,
700-
downloadCsv
700+
downloadCsv,
701+
calculateNiceScale
701702
} from '../lib';
702703
import mainConfig from "../default_configs.json";
703704
import DataTable from "../atoms/DataTable.vue";
@@ -812,11 +813,11 @@ export default {
812813
return to - from + 1;
813814
},
814815
relativeZero() {
815-
if(this.min >= 0) return 0;
816-
return Math.abs(this.min);
816+
if(this.niceScale.min >= 0) return 0;
817+
return Math.abs(this.niceScale.min);
817818
},
818819
absoluteMax() {
819-
return this.max + this.relativeZero;
820+
return this.niceScale.max + this.relativeZero;
820821
},
821822
safeDataset(){
822823
if(!this.useSafeValues) return this.dataset;
@@ -914,6 +915,9 @@ export default {
914915
if(min > 0) return 0;
915916
return min;
916917
},
918+
niceScale() {
919+
return this.calculateNiceScale(this.min, this.max, 10)
920+
},
917921
maxSeries(){
918922
return this.slicer.end - this.slicer.start;
919923
},
@@ -1047,25 +1051,12 @@ export default {
10471051
return `0 0 ${this.chartConfig.chart.width} ${this.chartConfig.chart.height}`;
10481052
},
10491053
yLabels() {
1050-
const positiveStep = this.closestDecimal(this.max / 5);
1051-
const positiveSteps = [];
1052-
for(let i = 5; i > 0; i -= 1) {
1053-
const value = positiveStep * i ;
1054-
positiveSteps.push({
1055-
y: this.zero - (this.drawingArea.height * this.ratioToMax(positiveStep * i)),
1056-
value,
1057-
});
1058-
}
1059-
const negativeStep = this.closestDecimal(this.min / 5);
1060-
const negativeSteps = [];
1061-
for(let i = 5; i >= 0; i -= 1) {
1062-
const value = Math.abs(negativeStep) * i ;
1063-
negativeSteps.push({
1064-
y: this.zero + (this.drawingArea.height * this.ratioToMax(Math.abs(negativeStep) * i)),
1065-
value: -value
1066-
});
1067-
}
1068-
return [...positiveSteps, ...negativeSteps];
1054+
return this.niceScale.ticks.map(t => {
1055+
return {
1056+
y: t >= 0 ? this.zero - (this.drawingArea.height * this.ratioToMax(t)) : this.zero + (this.drawingArea.height * this.ratioToMax(Math.abs(t))),
1057+
value: t
1058+
}
1059+
})
10691060
},
10701061
zero(){
10711062
return this.drawingArea.bottom - (this.drawingArea.height * this.ratioToMax(this.relativeZero));
@@ -1198,6 +1189,7 @@ export default {
11981189
}
11991190
},
12001191
methods: {
1192+
calculateNiceScale,
12011193
checkNaN,
12021194
createSmoothPath,
12031195
isSafeValue,

src/lib.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,55 @@ export function lightenHexColor(hexColor, percentLighter) {
783783
return lighterHex;
784784
}
785785

786+
export function niceNum(range, round) {
787+
const exponent = Math.floor(Math.log10(range));
788+
const fraction = range / Math.pow(10, exponent);
789+
let niceFraction;
790+
791+
if (round) {
792+
if (fraction < 1.5) {
793+
niceFraction = 1;
794+
} else if (fraction < 3) {
795+
niceFraction = 2;
796+
} else if (fraction < 7) {
797+
niceFraction = 5;
798+
} else {
799+
niceFraction = 10;
800+
}
801+
} else {
802+
if (fraction <= 1) {
803+
niceFraction = 1;
804+
} else if (fraction <= 2) {
805+
niceFraction = 2;
806+
} else if (fraction <= 5) {
807+
niceFraction = 5;
808+
} else {
809+
niceFraction = 10;
810+
}
811+
}
812+
813+
return niceFraction * Math.pow(10, exponent);
814+
}
815+
816+
export function calculateNiceScale(minValue, maxValue, maxTicks) {
817+
const range = niceNum(maxValue - minValue, false);
818+
const tickSpacing = niceNum(range / (maxTicks - 1), true);
819+
const niceMin = Math.floor(minValue / tickSpacing) * tickSpacing;
820+
const niceMax = Math.ceil(maxValue / tickSpacing) * tickSpacing;
821+
822+
const ticks = [];
823+
for (let tick = niceMin; tick <= niceMax; tick += tickSpacing) {
824+
ticks.push(tick);
825+
}
826+
827+
return {
828+
min: niceMin,
829+
max: niceMax,
830+
tickSize: tickSpacing,
831+
ticks
832+
};
833+
}
834+
786835
const lib = {
787836
adaptColorToBackground,
788837
addVector,

0 commit comments

Comments
 (0)