Skip to content

Commit 9bc51cf

Browse files
committed
VueUiWheel add component
1 parent 459a312 commit 9bc51cf

File tree

8 files changed

+383
-4
lines changed

8 files changed

+383
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Available components:
3939
- [VueUiRelationCircle](https://vue-data-ui.graphieros.com/docs#vue-ui-relation-circle)
4040
- [VueUiThermometer](https://vue-data-ui.graphieros.com/docs#vue-ui-thermometer)
4141
- [VueUiRings](https://vue-data-ui.graphieros.com/docs#vue-ui-rings)
42+
- [VueUiWheel](https://vue-data-ui.graphieros.com/docs#vue-ui-wheel)
4243

4344

4445
## Mini charts

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.24",
4+
"version": "1.9.25",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [
@@ -31,7 +31,8 @@
3131
"smiley",
3232
"relationship circle",
3333
"thermometer",
34-
"rings"
34+
"rings",
35+
"wheel"
3536
],
3637
"author": "Alec Lloyd Probert",
3738
"repository": {

src/App.vue

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import ThermoTest from "./components/vue-ui-thermometer.vue";
3939
import StackTest from "./components/vue-ui-sparkstackbar.vue";
4040
import HistoTest from "./components/vue-ui-sparkhistogram.vue";
4141
import RingsTest from "./components/vue-ui-rings.vue";
42+
import WheelTest from "./components/vue-ui-wheel.vue";
4243
4344
const dataset = ref([
4445
{
@@ -3475,6 +3476,12 @@ const ringsDataset = ref([
34753476
])
34763477
34773478
3479+
const wheelDataset = ref({
3480+
percentage: 66.5
3481+
})
3482+
3483+
const wheelConfig = ref(PROD_CONFIG.vvue_ui_wheel)
3484+
34783485
const showLocalTest = ref(false);
34793486
34803487
function copyConfig(c) {
@@ -3506,6 +3513,27 @@ const updateStep = ref(0);
35063513
<h4 style="color: #5f8bee">Manual testing arena</h4>
35073514
</div>
35083515

3516+
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_wheel)">
3517+
<template #title>VueUiWheel</template>
3518+
<template #dev>
3519+
<WheelTest
3520+
ref="rings"
3521+
:dataset="wheelDataset"
3522+
:config="{style:{chart:{title:{text:'Title',subtitle:{text:'Subtitle'}}}}}"
3523+
/>
3524+
</template>
3525+
<template #prod>
3526+
<VueUiWheel
3527+
ref="rings"
3528+
:dataset="wheelDataset"
3529+
:config="{style:{chart:{title:{text:'Title',subtitle:{text:'Subtitle'}}}}}"
3530+
/>
3531+
</template>
3532+
<template #config>
3533+
{{ PROD_CONFIG.vue_ui_wheel }}
3534+
</template>
3535+
</Box>
3536+
35093537
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_rings)">
35103538
<template #title>VueUiRings</template>
35113539
<template #dev>

src/components/vue-ui-wheel.vue

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<script setup>
2+
import { ref, computed, nextTick, onMounted } from "vue";
3+
import pdf from "../pdf";
4+
import img from "../img";
5+
import mainConfig from "../default_configs.json";
6+
import { useNestedProp } from "../useNestedProp";
7+
import Title from "../atoms/Title.vue";
8+
import UserOptions from "../atoms/UserOptions.vue";
9+
import { shiftHue } from "../lib";
10+
11+
const props = defineProps({
12+
config: {
13+
type: Object,
14+
default() {
15+
return {}
16+
}
17+
},
18+
dataset: {
19+
type: Object,
20+
default() {
21+
return {}
22+
}
23+
},
24+
});
25+
26+
const uid = ref(`vue-ui-wheel-${Math.random()}`);
27+
const defaultConfig = ref(mainConfig.vue_ui_wheel);
28+
29+
const isImaging = ref(false);
30+
const isPrinting = ref(false);
31+
const wheelChart = ref(null);
32+
const details = ref(null);
33+
34+
35+
const wheelConfig = computed(() => {
36+
return useNestedProp({
37+
userConfig: props.config,
38+
defaultConfig: defaultConfig.value
39+
});
40+
});
41+
42+
const svg = ref({
43+
size: 360
44+
})
45+
46+
const wheel = computed(() => {
47+
return {
48+
radius: (svg.value.size * 0.9) / 2,
49+
centerX: svg.value.size / 2,
50+
centerY: svg.value.size / 2,
51+
}
52+
})
53+
54+
function calcTickStart(angle, distance = 1) {
55+
const angleStart = 29.85;
56+
return {
57+
x: wheel.value.centerX + wheel.value.radius * Math.cos(angleStart + angle * Math.PI / 180) * distance,
58+
y: wheel.value.centerY + wheel.value.radius * Math.sin(angleStart + angle * Math.PI / 180) * distance
59+
}
60+
}
61+
62+
const activeValue = ref(wheelConfig.value.style.chart.animation.use ? 0 : props.dataset.percentage);
63+
64+
onMounted(() => {
65+
let acceleration = 0;
66+
let speed = wheelConfig.value.style.chart.animation.speed;
67+
let incr = (0.005) * wheelConfig.value.style.chart.animation.acceleration;
68+
function animate() {
69+
activeValue.value += speed + acceleration;
70+
acceleration += incr;
71+
if(activeValue.value < props.dataset.percentage) {
72+
requestAnimationFrame(animate)
73+
} else {
74+
activeValue.value = props.dataset.percentage
75+
}
76+
}
77+
78+
if(wheelConfig.value.style.chart.animation.use) {
79+
activeValue.value = 0;
80+
animate()
81+
}
82+
})
83+
84+
const ticks = computed(() => {
85+
const tickArray = [];
86+
const tickAmount = 100;
87+
for(let i = 0; i < tickAmount; i += 1) {
88+
const color = activeValue.value > i ? wheelConfig.value.style.chart.layout.wheel.ticks.activeColor : wheelConfig.value.style.chart.layout.wheel.ticks.inactiveColor;
89+
tickArray.push({
90+
x1: calcTickStart((360 / tickAmount) * i).x,
91+
y1: calcTickStart((360 / tickAmount) * i).y,
92+
x2: calcTickStart((360 / tickAmount) * i, 0.9).x,
93+
y2: calcTickStart((360 / tickAmount) * i, 0.9).y,
94+
color: wheelConfig.value.style.chart.layout.wheel.ticks.gradient.show ? shiftHue(color, i / tickAmount * (wheelConfig.value.style.chart.layout.wheel.ticks.gradient.shiftHueIntensity / 100)) : color
95+
})
96+
}
97+
return tickArray
98+
})
99+
100+
const __to__ = ref(null);
101+
102+
function showSpinnerPdf() {
103+
isPrinting.value = true;
104+
}
105+
106+
function generatePdf(){
107+
showSpinnerPdf();
108+
clearTimeout(__to__.value);
109+
__to__.value = setTimeout(() => {
110+
pdf({
111+
domElement: document.getElementById(`${uid.value}`),
112+
fileName: wheelConfig.value.style.chart.title.text || 'vue-ui-wheel'
113+
}).finally(() => {
114+
isPrinting.value = false;
115+
})
116+
}, 100)
117+
}
118+
119+
function showSpinnerImage() {
120+
isImaging.value = true;
121+
}
122+
123+
function generateImage() {
124+
showSpinnerImage();
125+
clearTimeout(__to__.value);
126+
__to__.value = setTimeout(() => {
127+
img({
128+
domElement: document.getElementById(`${uid.value}`),
129+
fileName: wheelConfig.value.style.chart.title.text || 'vue-ui-wheel',
130+
format: 'png'
131+
}).finally(() => {
132+
isImaging.value = false;
133+
})
134+
}, 100)
135+
}
136+
137+
</script>
138+
139+
<template>
140+
<div
141+
class="vue-ui-wheel"
142+
ref="wheelChart"
143+
:id="uid"
144+
:style="`font-family:${wheelConfig.style.fontFamily};width:100%; text-align:center;${wheelConfig.userOptions.show ? 'padding-top:36px' : ''}`"
145+
>
146+
147+
<div v-if="wheelConfig.style.chart.title.text" :style="`width:100%;background:${wheelConfig.style.chart.backgroundColor};padding-bottom:12px`">
148+
<Title
149+
:config="{
150+
title: {
151+
cy: 'wheel-title',
152+
text: wheelConfig.style.chart.title.text,
153+
color: wheelConfig.style.chart.title.color,
154+
fontSize: wheelConfig.style.chart.title.fontSize,
155+
bold: wheelConfig.style.chart.title.bold
156+
},
157+
subtitle: {
158+
cy: 'wheel-subtitle',
159+
text: wheelConfig.style.chart.title.subtitle.text,
160+
color: wheelConfig.style.chart.title.subtitle.color,
161+
fontSize: wheelConfig.style.chart.title.subtitle.fontSize,
162+
bold: wheelConfig.style.chart.title.subtitle.bold
163+
},
164+
}"
165+
/>
166+
</div>
167+
168+
<UserOptions
169+
ref="details"
170+
v-if="wheelConfig.userOptions.show"
171+
:backgroundColor="wheelConfig.style.chart.backgroundColor"
172+
:color="wheelConfig.style.chart.color"
173+
:isPrinting="isPrinting"
174+
:isImaging="isImaging"
175+
:title="wheelConfig.userOptions.title"
176+
:uid="uid"
177+
:hasImg="true"
178+
:hasXls="false"
179+
@generatePdf="generatePdf"
180+
@generateImage="generateImage"
181+
/>
182+
183+
<svg data-cy="wheel-svg" :viewBox="`0 0 ${svg.size} ${svg.size}`" :style="`max-width:100%;overflow:visible;background:${wheelConfig.style.chart.backgroundColor};color:${wheelConfig.style.chart.color}`">
184+
<line
185+
v-for="(tick, i) in ticks"
186+
:x1="tick.x1"
187+
:x2="tick.x2"
188+
:y1="tick.y1"
189+
:y2="tick.y2"
190+
:stroke="tick.color"
191+
:stroke-width="5"
192+
:stroke-linecap="wheelConfig.style.chart.layout.wheel.ticks.rounded ? 'round' : 'butt'"
193+
/>
194+
<circle
195+
v-if="wheelConfig.style.chart.layout.innerCircle.show"
196+
:cx="wheel.centerX"
197+
:cy="wheel.centerY"
198+
:r="wheel.radius * 0.8"
199+
:stroke="wheelConfig.style.chart.layout.innerCircle.stroke"
200+
:stroke-width="wheelConfig.style.chart.layout.innerCircle.strokeWidth"
201+
fill="none"
202+
/>
203+
<text
204+
v-if="wheelConfig.style.chart.layout.percentage.show"
205+
:x="wheel.centerX"
206+
:y="wheel.centerY + wheelConfig.style.chart.layout.percentage.fontSize / 3"
207+
:font-size="wheelConfig.style.chart.layout.percentage.fontSize"
208+
:fill="wheelConfig.style.chart.layout.wheel.ticks.gradient.show ? shiftHue(wheelConfig.style.chart.layout.wheel.ticks.activeColor, activeValue / 100 * (wheelConfig.style.chart.layout.wheel.ticks.gradient.shiftHueIntensity / 100)) : wheelConfig.style.chart.layout.wheel.ticks.activeColor"
209+
text-anchor="middle"
210+
:font-weight="wheelConfig.style.chart.layout.percentage.bold ? 'bold' : 'normal'"
211+
>
212+
{{ Number(activeValue.toFixed(wheelConfig.style.chart.layout.percentage.rounding)).toLocaleString() }}%
213+
</text>
214+
</svg>
215+
216+
</div>
217+
</template>
218+
219+
<style scoped>
220+
.vue-ui-wheel *{
221+
transition: unset;
222+
}
223+
.vue-ui-wheel {
224+
user-select: none;
225+
position: relative;
226+
}
227+
</style>

src/default_configs.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2501,5 +2501,59 @@
25012501
"roundingPercentage": 0
25022502
}
25032503
}
2504+
},
2505+
"vue_ui_wheel": {
2506+
"style": {
2507+
"fontFamily":"inherit",
2508+
"chart": {
2509+
"backgroundColor":"#FFFFFF",
2510+
"color":"#2D353C",
2511+
"animation": {
2512+
"use": true,
2513+
"speed": 0.5,
2514+
"acceleration": 1
2515+
},
2516+
"layout": {
2517+
"wheel": {
2518+
"ticks": {
2519+
"rounded": true,
2520+
"inactiveColor": "#e1e5e8",
2521+
"activeColor": "#5f8bee",
2522+
"gradient": {
2523+
"show": true,
2524+
"shiftHueIntensity": 100
2525+
}
2526+
}
2527+
},
2528+
"innerCircle": {
2529+
"show": true,
2530+
"stroke": "#e1e5e8",
2531+
"strokeWidth": 1
2532+
},
2533+
"percentage": {
2534+
"show": true,
2535+
"fontSize": 48,
2536+
"rounding": 1,
2537+
"bold": true
2538+
}
2539+
},
2540+
"title": {
2541+
"text": "",
2542+
"color": "#2D353C",
2543+
"fontSize": 20,
2544+
"bold": true,
2545+
"subtitle": {
2546+
"color": "#A1A1A1",
2547+
"text": "",
2548+
"fontSize": 16,
2549+
"bold": false
2550+
}
2551+
}
2552+
}
2553+
},
2554+
"userOptions": {
2555+
"show": true,
2556+
"title": "options"
2557+
}
25042558
}
25052559
}

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import VueUiThermometer from "./components/vue-ui-thermometer.vue";
2525
import VueUiSparkStackbar from "./components/vue-ui-sparkstackbar.vue";
2626
import VueUiSparkHistogram from "./components/vue-ui-sparkhistogram.vue";
2727
import VueUiRings from "./components/vue-ui-rings.vue";
28+
import VueUiWheel from "./components/vue-ui-wheel.vue";
2829

2930
export {
3031
VueUiXy,
@@ -53,5 +54,6 @@ export {
5354
VueUiThermometer,
5455
VueUiSparkStackbar,
5556
VueUiSparkHistogram,
56-
VueUiRings
57+
VueUiRings,
58+
VueUiWheel
5759
};

src/main.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import {
2828
VueUiThermometer,
2929
VueUiSparkStackbar,
3030
VueUiSparkHistogram,
31-
VueUiRings
31+
VueUiRings,
32+
VueUiWheel
3233
} from 'vue-data-ui';
3334
import 'vue-data-ui/style.css';
3435

@@ -60,4 +61,5 @@ app.component("VueUiThermometer", VueUiThermometer);
6061
app.component("VueUiSparkStackbar", VueUiSparkStackbar);
6162
app.component("VueUiSparkHistogram", VueUiSparkHistogram);
6263
app.component("VueUiRings", VueUiRings);
64+
app.component("VueUiWheel", VueUiWheel);
6365
app.mount('#app');

0 commit comments

Comments
 (0)