Skip to content

Commit 7eeb5ad

Browse files
committed
VueUi3dBar added component
1 parent ccc52fc commit 7eeb5ad

File tree

15 files changed

+434
-34
lines changed

15 files changed

+434
-34
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ Available components:
5151
- [VueUiSparkstackbar](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkstackbar)
5252
- [VueUiSparkHistogram](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkhistogram)
5353

54+
## 3d
55+
- [VueUi3dBar](https://vue-data-ui.graphieros.com/docs#vue-ui-3d-bar)
56+
5457
## Tables
5558
- [VueUiTable](https://vue-data-ui.graphieros.com/docs#vue-ui-table)
5659

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: 3 additions & 2 deletions
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.44",
4+
"version": "1.9.45",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [
@@ -35,7 +35,8 @@
3535
"wheel",
3636
"tiremarks",
3737
"donut evolution",
38-
"mood radar"
38+
"mood radar",
39+
"3d bar"
3940
],
4041
"author": "Alec Lloyd Probert",
4142
"repository": {

src/App.vue

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import RingsTest from "./components/vue-ui-rings.vue";
4343
import WheelTest from "./components/vue-ui-wheel.vue";
4444
import TireTest from "./components/vue-ui-tiremarks.vue";
4545
import MoodRadarTest from "./components/vue-ui-mood-radar.vue";
46+
import Bar3dTest from "./components/vue-ui-3d-bar.vue";
4647
import DonutEvolutionTest from "./components/vue-ui-donut-evolution.vue";
4748
4849
const dataset = ref([
@@ -3658,6 +3659,10 @@ const moodRadarConfig = ref({
36583659
}
36593660
})
36603661
3662+
const bar3dDataset = ref({
3663+
percentage: 66
3664+
})
3665+
36613666
</script>
36623667

36633668
<template>
@@ -3726,6 +3731,7 @@ const moodRadarConfig = ref({
37263731
<BaseIcon name="chartHeatmap" stroke="#42d392" />
37273732
<BaseIcon name="chartTable" stroke="#42d392" />
37283733
<BaseIcon name="chartMoodRadar" stroke="#42d392" />
3734+
<BaseIcon name="chart3dBar" stroke="#42d392" />
37293735
</div>
37303736
</template>
37313737
</Box>
@@ -3752,6 +3758,27 @@ const moodRadarConfig = ref({
37523758

37533759
</Box>
37543760

3761+
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_3d_bar)">
3762+
<template #title>
3763+
<BaseIcon name="chart3dBar"/>
3764+
VueUi3dBar
3765+
</template>
3766+
<template #dev>
3767+
<Bar3dTest :dataset="bar3dDataset">
3768+
<template #svg="{ svg }">
3769+
<circle :cx="svg.width / 2" :cy="svg.height / 2" :r="30" fill="#FF000033"/>
3770+
</template>
3771+
</Bar3dTest>
3772+
</template>
3773+
<template #prod>
3774+
<VueUi3dBar :dataset="bar3dDataset">
3775+
<template #svg="{ svg }">
3776+
<circle :cx="svg.width / 2" :cy="svg.height / 2" :r="30" fill="#FF000033"/>
3777+
</template>
3778+
</VueUi3dBar>
3779+
</template>
3780+
</Box>
3781+
37553782
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_screenshot)">
37563783
<template #title>
37573784
<BaseIcon name="screenshot"/>
@@ -3768,7 +3795,6 @@ const moodRadarConfig = ref({
37683795
<template #config>
37693796
{{ PROD_CONFIG.vue_ui_screenshot }}
37703797
</template>
3771-
37723798
</Box>
37733799

37743800
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_donut_evolution)">

src/atoms/BaseIcon.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ const icons = computed(() => {
6767
moodFlat: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" stroke-linecap="round" d="M1 10A1 1 0 0019 10 1 1 0 001 10M5 13C8 11 12 11 15 13M5 7A1 1 0 008 7 1 1 0 005 7M12 7A1 1 0 0015 7 1 1 0 0012 7" />`,
6868
moodNeutral: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" stroke-linecap="round" d="M1 10A1 1 0 0019 10 1 1 0 001 10M5 12C9 12 11 12 15 12M5 7A1 1 0 008 7 1 1 0 005 7M12 7A1 1 0 0015 7 1 1 0 0012 7" />`,
6969
moodHappy: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" stroke-linecap="round" d="M1 10A1 1 0 0019 10 1 1 0 001 10M5 11C6 18 14 18 15 11L5 11M5 7A1 1 0 008 7 1 1 0 005 7M12 7A1 1 0 0015 7 1 1 0 0012 7" />`,
70-
chartMoodRadar: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" stroke-linecap="round" stroke-linejoin="round" d="M10 4 4 9 6 16 14 16 16 9 10 4M9 2A1 1 0 0011 2 1 1 0 009 2M1 8A1 1 0 003 8 1 1 0 001 8M3 17A1 1 0 005 17 1 1 0 003 17M15 17A1 1 0 0017 17 1 1 0 0015 17M17 8A1 1 0 0019 8 1 1 0 0017 8M10 7 13 10 13 14 8 13 7 9 10 7" />`
70+
chartMoodRadar: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" stroke-linecap="round" stroke-linejoin="round" d="M10 4 4 9 6 16 14 16 16 9 10 4M9 2A1 1 0 0011 2 1 1 0 009 2M1 8A1 1 0 003 8 1 1 0 001 8M3 17A1 1 0 005 17 1 1 0 003 17M15 17A1 1 0 0017 17 1 1 0 0015 17M17 8A1 1 0 0019 8 1 1 0 0017 8M10 7 13 10 13 14 8 13 7 9 10 7" />`,
71+
chart3dBar: `<path fill="none" stroke="${props.stroke}" stroke-width="${props.strokeWidth}" d="M10 1 6 3 6 17 10 19 14 17 14 3 10 1M6 3 10 5 14 3M10 5 10 19" stroke-linecap="round" stroke-linejoin="round" />`
7172
}
7273
})
7374

src/atoms/Title.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,22 @@ const CONFIG = useNestedProp({
3535
<template>
3636
<div
3737
:data-cy="CONFIG.title.cy"
38-
:style="`width:100%;text-align:center;color:${
38+
:style="`width:calc(100% - 72px);text-align:center;color:${
3939
CONFIG.title.color
4040
};font-size:${CONFIG.title.fontSize}px;font-weight:${
4141
CONFIG.title.bold ? 'bold' : ''
42-
}`"
42+
};padding: 0 36px`"
4343
>
4444
{{ CONFIG.title.text }}
4545
</div>
4646
<div
4747
:data-cy="CONFIG.subtitle.cy"
4848
v-if="CONFIG.subtitle.text"
49-
:style="`width:100%;text-align:center;color:${
49+
:style="`width:calc(100% - 72px);text-align:center;color:${
5050
CONFIG.subtitle.color
5151
};font-size:${CONFIG.subtitle.fontSize}px;font-weight:${
5252
CONFIG.subtitle.bold ? 'bold' : ''
53-
}`"
53+
};padding: 0 36px`"
5454
>
5555
{{ CONFIG.subtitle.text }}
5656
</div>

src/components/vue-ui-3d-bar.vue

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<script setup>
2+
import { ref, computed, onMounted } from "vue";
3+
import { calcMarkerOffsetX, calcMarkerOffsetY, calcNutArrowPath, makeDonut, palette, convertColorToHex, opacity, makeXls, createUid } from '../lib';
4+
import pdf from "../pdf";
5+
import img from "../img";
6+
import mainConfig from "../default_configs.json";
7+
import Title from "../atoms/Title.vue";
8+
import { useNestedProp } from "../useNestedProp";
9+
import UserOptions from "../atoms/UserOptions.vue";
10+
import DataTable from "../atoms/DataTable.vue";
11+
import Tooltip from "../atoms/Tooltip.vue";
12+
import Legend from "../atoms/Legend.vue";
13+
14+
const props = defineProps({
15+
config: {
16+
type: Object,
17+
default() {
18+
return {}
19+
}
20+
},
21+
dataset: {
22+
type: Object,
23+
default() {
24+
return {}
25+
}
26+
},
27+
});
28+
29+
const uid = ref(createUid());
30+
31+
const defaultConfig = ref(mainConfig.vue_ui_3d_bar);
32+
33+
const isPrinting = ref(false);
34+
const isImaging = ref(false);
35+
const bar3dChart = ref(null);
36+
const details = ref(null);
37+
const isTooltip = ref(false);
38+
const tooltipContent = ref("");
39+
const selectedSerie = ref(null);
40+
41+
const barConfig = computed(() => {
42+
return useNestedProp({
43+
userConfig: props.config,
44+
defaultConfig: defaultConfig.value
45+
});
46+
});
47+
48+
const svg = computed(() => {
49+
return {
50+
height: barConfig.value.style.chart.box.dimensions.height,
51+
width: barConfig.value.style.chart.box.dimensions.width,
52+
top: barConfig.value.style.chart.box.dimensions.top,
53+
bottom: barConfig.value.style.chart.box.dimensions.bottom,
54+
left: barConfig.value.style.chart.box.dimensions.left,
55+
right: barConfig.value.style.chart.box.dimensions.right,
56+
perspective: barConfig.value.style.chart.box.dimensions.perspective
57+
}
58+
});
59+
60+
const box = computed(() => {
61+
return {
62+
right: `M${svg.value.width / 2},${svg.value.top} ${svg.value.width - svg.value.right}, ${svg.value.top + svg.value.perspective} ${svg.value.width - svg.value.right},${svg.value.height - svg.value.bottom - svg.value.perspective} ${svg.value.width / 2},${svg.value.height - svg.value.bottom}`,
63+
left: `M${svg.value.width / 2},${svg.value.top} ${svg.value.left},${svg.value.top + svg.value.perspective} ${svg.value.left},${svg.value.height - svg.value.bottom - svg.value.perspective} ${svg.value.width / 2},${svg.value.height - svg.value.bottom}`,
64+
side: `M${svg.value.width / 2},${svg.value.height - svg.value.bottom} ${svg.value.width / 2},${svg.value.top + (svg.value.perspective * 2)}`,
65+
topSides: `M${svg.value.left},${svg.value.top + svg.value.perspective} ${svg.value.width / 2},${svg.value.top + (svg.value.perspective * 2)} ${svg.value.width - svg.value.right},${svg.value.top + svg.value.perspective}`
66+
}
67+
});
68+
69+
const activeValue = ref(barConfig.value.style.chart.animation.use ? 0 : props.dataset.percentage);
70+
71+
onMounted(() => {
72+
let acceleration = 0;
73+
let speed = barConfig.value.style.chart.animation.speed;
74+
let incr = (0.005) * barConfig.value.style.chart.animation.acceleration;
75+
function animate() {
76+
activeValue.value += speed + acceleration;
77+
acceleration += incr;
78+
if(activeValue.value < props.dataset.percentage) {
79+
requestAnimationFrame(animate)
80+
} else {
81+
activeValue.value = props.dataset.percentage
82+
}
83+
}
84+
85+
if(barConfig.value.style.chart.animation.use) {
86+
activeValue.value = 0;
87+
animate()
88+
}
89+
})
90+
91+
const fill = computed(() => {
92+
const proportion = activeValue.value / 100;
93+
const height = svg.value.height - svg.value.bottom - svg.value.top - (svg.value.perspective * 2);
94+
return {
95+
right: `M${svg.value.width / 2},${svg.value.height - svg.value.bottom} ${svg.value.width / 2},${svg.value.height - svg.value.bottom - height * proportion} ${svg.value.width - svg.value.right},${svg.value.height - svg.value.bottom - svg.value.perspective - height * proportion} ${svg.value.width - svg.value.right},${svg.value.height - svg.value.bottom - svg.value.perspective}Z`,
96+
left: `M${svg.value.width / 2},${svg.value.height - svg.value.bottom} ${svg.value.width / 2},${svg.value.height - svg.value.bottom - height * proportion} ${svg.value.left}, ${svg.value.height - svg.value.bottom - svg.value.perspective - height * proportion} ${svg.value.left},${svg.value.height - svg.value.bottom - svg.value.perspective}Z`,
97+
top: `M${svg.value.width / 2},${svg.value.height - svg.value.bottom - height * proportion} ${svg.value.left},${svg.value.height - svg.value.bottom - svg.value.perspective - height * proportion} ${svg.value.width / 2},${svg.value.height - svg.value.bottom - (svg.value.perspective * 2) - (height * proportion)} ${svg.value.width - svg.value.right},${svg.value.height - svg.value.bottom - svg.value.perspective - height * proportion} Z`
98+
}
99+
})
100+
101+
const __to__ = ref(null);
102+
103+
function showSpinnerPdf() {
104+
isPrinting.value = true;
105+
}
106+
107+
function generatePdf(){
108+
showSpinnerPdf();
109+
clearTimeout(__to__.value);
110+
__to__.value = setTimeout(() => {
111+
pdf({
112+
domElement: document.getElementById(`3d_bar_${uid.value}`),
113+
fileName: barConfig.value.style.chart.title.text || 'vue-ui-3d-bar'
114+
}).finally(() => {
115+
isPrinting.value = false;
116+
});
117+
}, 100)
118+
119+
}
120+
121+
function showSpinnerImage() {
122+
isImaging.value = true;
123+
}
124+
125+
function generateImage() {
126+
showSpinnerImage();
127+
clearTimeout(__to__.value);
128+
__to__.value = setTimeout(() => {
129+
img({
130+
domElement: document.getElementById(`3d_bar_${uid.value}`),
131+
fileName: barConfig.value.style.chart.title.text || 'vue-ui-3d-bar',
132+
format: 'png'
133+
}).finally(() => {
134+
isImaging.value = false;
135+
})
136+
}, 100)
137+
}
138+
139+
defineExpose({
140+
generatePdf,
141+
generateImage
142+
});
143+
144+
145+
</script>
146+
147+
<template>
148+
<div :ref="`bar3dChart`" :class="`vue-ui-3d-bar`" :style="`font-family:${barConfig.style.fontFamily};width:100%; text-align:center;background:${barConfig.style.chart.backgroundColor}`" :id="`3d_bar_${uid}`">
149+
150+
<div v-if="barConfig.style.chart.title.text" :style="`width:100%;background:${barConfig.style.chart.backgroundColor}`">
151+
<!-- TITLE AS DIV -->
152+
<Title
153+
:config="{
154+
title: {
155+
cy: '3dBar-div-title',
156+
text: barConfig.style.chart.title.text,
157+
color: barConfig.style.chart.title.color,
158+
fontSize: barConfig.style.chart.title.fontSize,
159+
bold: barConfig.style.chart.title.bold
160+
},
161+
subtitle: {
162+
cy: '3dBar-div-subtitle',
163+
text: barConfig.style.chart.title.subtitle.text,
164+
color: barConfig.style.chart.title.subtitle.color,
165+
fontSize: barConfig.style.chart.title.subtitle.fontSize,
166+
bold: barConfig.style.chart.title.subtitle.bold
167+
}
168+
}"
169+
/>
170+
</div>
171+
172+
<!-- OPTIONS -->
173+
<UserOptions
174+
ref="details"
175+
v-if="barConfig.userOptions.show"
176+
:backgroundColor="barConfig.style.chart.backgroundColor"
177+
:color="barConfig.style.chart.color"
178+
:isPrinting="isPrinting"
179+
:isImaging="isImaging"
180+
:title="barConfig.userOptions.title"
181+
:uid="uid"
182+
hasImg
183+
:hasXls="false"
184+
@generatePdf="generatePdf"
185+
@generateImage="generateImage"
186+
/>
187+
188+
<svg data-cy="3d-bar-svg" :viewBox="`0 0 ${svg.width} ${svg.height}`" :style="`max-width:100%; overflow: visible; background:${barConfig.style.chart.backgroundColor};color:${barConfig.style.chart.color}`">
189+
190+
<!-- DEFS -->
191+
<defs>
192+
<radialGradient :id="`gradient_top${uid}`">
193+
<stop offset="0%" :stop-color="`${convertColorToHex(barConfig.style.chart.backgroundColor)}00`" />
194+
<stop offset="100%" :stop-color="`${barConfig.style.chart.bar.color}`" />
195+
</radialGradient>
196+
<radialGradient :id="`gradient_left${uid}`">
197+
<stop offset="0%" :stop-color="`${convertColorToHex(barConfig.style.chart.backgroundColor)}00`" />
198+
<stop offset="100%" :stop-color="`${barConfig.style.chart.bar.color}33`" />
199+
</radialGradient>
200+
<radialGradient :id="`gradient_right${uid}`">
201+
<stop offset="0%" :stop-color="`${convertColorToHex(barConfig.style.chart.backgroundColor)}00`" />
202+
<stop offset="100%" :stop-color="`${barConfig.style.chart.bar.color}33`" />
203+
</radialGradient>
204+
</defs>
205+
206+
<text
207+
:x="svg.width / 2"
208+
:y="svg.top - barConfig.style.chart.dataLabel.fontSize / 2"
209+
:font-size="barConfig.style.chart.dataLabel.fontSize"
210+
:font-weight="barConfig.style.chart.dataLabel.bold ? 'bold': 'normal'"
211+
:fill="barConfig.style.chart.dataLabel.color"
212+
text-anchor="middle"
213+
>
214+
{{Number((isNaN(activeValue) ? 0 : activeValue).toFixed(barConfig.style.chart.dataLabel.rounding)).toLocaleString() }} %
215+
</text>
216+
217+
<!-- BOX SKELETON -->
218+
<path :stroke-dasharray="barConfig.style.chart.box.strokeDasharray" :d="box.right" :stroke="barConfig.style.chart.box.stroke" stroke-linejoin="round" stroke-linecap="round" fill="none"/>
219+
<path :stroke-dasharray="barConfig.style.chart.box.strokeDasharray" :d="box.left" :stroke="barConfig.style.chart.box.stroke" stroke-linejoin="round" stroke-linecap="round" fill="none"/>
220+
<path :stroke-dasharray="barConfig.style.chart.box.strokeDasharray" :d="box.side" :stroke="barConfig.style.chart.box.stroke" stroke-linejoin="round" stroke-linecap="round" fill="none"/>
221+
<path :stroke-dasharray="barConfig.style.chart.box.strokeDasharray" :d="box.topSides" :stroke="barConfig.style.chart.box.stroke" stroke-linejoin="round" stroke-linecap="round" fill="none"/>
222+
223+
<!-- FILL BOX -->
224+
<path :d="fill.right" :stroke="barConfig.style.chart.bar.stroke" :stroke-width="barConfig.style.chart.bar.strokeWidth" stroke-linejoin="round" stroke-linecap="round" :fill="`url(#gradient_right${uid})`"/>
225+
<path :d="fill.left" :stroke="barConfig.style.chart.bar.stroke" :stroke-width="barConfig.style.chart.bar.strokeWidth" stroke-linejoin="round" stroke-linecap="round" :fill="`url(#gradient_left${uid})`"/>
226+
<path :d="fill.top" :stroke="barConfig.style.chart.bar.stroke" :stroke-width="barConfig.style.chart.bar.strokeWidth" stroke-linejoin="round" stroke-linecap="round" :fill="`url(#gradient_top${uid})`"/>
227+
228+
<slot name="svg" :svg="svg"/>
229+
</svg>
230+
</div>
231+
</template>
232+
233+
<style scoped>
234+
.vue-ui-3d-bar *{
235+
transition: unset;
236+
}
237+
.vue-ui-3d-bar {
238+
user-select: none;
239+
position: relative;
240+
}
241+
</style>

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,6 @@ const table = computed(() => {
310310
}
311311
})).concat(['Σ']);
312312
let body = [];
313-
let time = [];
314313
315314
for(let i = 0; i < maxLength.value; i += 1) {
316315
const sum = convertedDataset.value.filter(ds => !segregated.value.includes(ds.uid)).map(ds => {

0 commit comments

Comments
 (0)