Skip to content

Commit 8c0afe7

Browse files
committed
VueUiSparkgauge added component
1 parent 426cea8 commit 8c0afe7

File tree

10 files changed

+300
-8
lines changed

10 files changed

+300
-8
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
![GitHub issues](https://img.shields.io/github/issues/graphieros/vue-data-ui)
1414
![NPM](https://img.shields.io/npm/l/vue-data-ui)
1515
![npm](https://img.shields.io/npm/dt/vue-data-ui)
16-
![Static Badge](https://img.shields.io/badge/components-38-green)
16+
![Static Badge](https://img.shields.io/badge/components-39-green)
1717

1818
[Interactive documentation](https://vue-data-ui.graphieros.com/)
1919

@@ -52,6 +52,7 @@ Available components:
5252
- [VueUiSparkbar](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkbar)
5353
- [VueUiSparkstackbar](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkstackbar)
5454
- [VueUiSparkHistogram](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkhistogram)
55+
- [VueUiSparkgauge](https://vue-data-ui.graphieros.com/docs#vue-ui-sparkgauge)
5556

5657
## 3d
5758

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": "2.0.30",
4+
"version": "2.0.32",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [

src/App.vue

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import TableSparklineTest from "./components/vue-ui-table-sparkline.vue";
5151
import MiniLoaderTest from "./components/vue-ui-mini-loader.vue";
5252
import NestedDonutsTest from "./components/vue-ui-nested-donuts.vue";
5353
import { getVueDataUiConfig } from "vue-data-ui";
54+
import SparkGaugeTest from "./components/vue-ui-sparkgauge.vue";
5455
5556
const dataset = ref([
5657
{
@@ -2725,6 +2726,15 @@ const nestedDonutsDataset = ref([
27252726
// },
27262727
])
27272728
2729+
const sparkGaugeDataset = ref({
2730+
value: 10,
2731+
min: -10,
2732+
max: 10,
2733+
title: "Some KPI with a long name"
2734+
})
2735+
2736+
const sparkGaugeConfig = ref({})
2737+
27282738
</script>
27292739

27302740
<template>
@@ -2826,6 +2836,24 @@ const nestedDonutsDataset = ref([
28262836
</template>
28272837
</Box>
28282838

2839+
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_sparkgauge)">
2840+
<template #title>
2841+
<BaseIcon name="chartGauge"/>
2842+
VueUiSparkgauge
2843+
</template>
2844+
<template #info>
2845+
</template>
2846+
<template #dev>
2847+
<SparkGaugeTest :dataset="sparkGaugeDataset" :config="sparkGaugeConfig" />
2848+
</template>
2849+
<template #prod>
2850+
<VueUiSparkgauge :dataset="sparkGaugeDataset" :config="sparkGaugeConfig" />
2851+
</template>
2852+
<template #config>
2853+
{{ PROD_CONFIG.vue_ui_sparkgauge }}
2854+
</template>
2855+
</Box>
2856+
28292857
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_nested_donuts)">
28302858
<template #title>
28312859
<BaseIcon name="chartNestedDonuts"/>
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<script setup>
2+
import { ref, computed, onMounted } from "vue";
3+
import mainConfig from "../default_configs.json";
4+
import { useNestedProp } from "../useNestedProp";
5+
import { createUid, dataLabel, interpolateColorHex } from "../lib";
6+
7+
const props = defineProps({
8+
config: {
9+
type: Object,
10+
default() {
11+
return {}
12+
}
13+
},
14+
dataset: {
15+
type: Object,
16+
default(){
17+
return {}
18+
}
19+
}
20+
})
21+
22+
const defaultConfig = ref(mainConfig.vue_ui_sparkgauge);
23+
24+
const sparkgaugeConfig = computed(() => {
25+
return useNestedProp({
26+
userConfig: props.config,
27+
defaultConfig: defaultConfig.value
28+
})
29+
});
30+
31+
const svg = computed(() => {
32+
return {
33+
height: sparkgaugeConfig.value.style.height,
34+
width: 128,
35+
base: sparkgaugeConfig.value.style.basePosition
36+
}
37+
})
38+
39+
console.log(svg.value)
40+
41+
const bounds = computed(() => {
42+
const min = props.dataset.min ?? 0;
43+
const max = props.dataset.max ?? 0;
44+
const diff = max - min;
45+
return {
46+
min,
47+
max,
48+
diff
49+
}
50+
})
51+
52+
const currentScore = ref(sparkgaugeConfig.value.style.animation.show ? bounds.value.min : props.dataset.value);
53+
54+
const animationTick = computed(() => {
55+
return bounds.value.diff / sparkgaugeConfig.value.style.animation.speedMs;
56+
})
57+
58+
onMounted(() => {
59+
function animate() {
60+
currentScore.value += animationTick.value;
61+
if(currentScore.value < props.dataset.value) {
62+
requestAnimationFrame(animate)
63+
} else {
64+
currentScore.value = props.dataset.value
65+
}
66+
}
67+
if(sparkgaugeConfig.value.style.animation.show) {
68+
currentScore.value = bounds.value.min;
69+
animate();
70+
}
71+
})
72+
73+
const nameLabel = computed(() => {
74+
return props.dataset.title ?? ''
75+
})
76+
77+
const valueRatio = computed(() => {
78+
if(currentScore.value >= 0) {
79+
return (currentScore.value - bounds.value.min) / bounds.value.diff
80+
} else {
81+
return (Math.abs(bounds.value.min) - Math.abs(currentScore.value)) / bounds.value.diff
82+
}
83+
})
84+
85+
const currentColor = computed(() => {
86+
return interpolateColorHex(sparkgaugeConfig.value.style.colors.min, sparkgaugeConfig.value.style.colors.max, bounds.value.min, bounds.value.max, currentScore.value)
87+
})
88+
89+
const labelColor = computed(() => {
90+
if(!sparkgaugeConfig.value.style.dataLabel.autoColor) {
91+
return sparkgaugeConfig.value.style.dataLabel.color;
92+
} else {
93+
return currentColor.value;
94+
}
95+
})
96+
97+
const trackColor = computed(() => {
98+
if (!sparkgaugeConfig.value.style.track.autoColor) {
99+
return sparkgaugeConfig.value.style.track.color;
100+
} else {
101+
return currentColor.value;
102+
}
103+
})
104+
</script>
105+
106+
<template>
107+
<div :style="`font-family:${sparkgaugeConfig.style.fontFamily};width: 100%; background:${sparkgaugeConfig.style.background}`">
108+
<!-- TITLE TOP -->
109+
<div v-if="sparkgaugeConfig.style.title.show && nameLabel && sparkgaugeConfig.style.title.position === 'top'" class="vue-data-ui-sparkgauge-label" :style="`font-size:${sparkgaugeConfig.style.title.fontSize}px;text-align:${sparkgaugeConfig.style.title.textAlign};font-weight:${sparkgaugeConfig.style.title.bold ? 'bold': 'normal'};©`">
110+
{{ nameLabel }}
111+
</div>
112+
<svg :viewBox="`0 0 ${svg.width} ${svg.height}`" :style="`overflow: visible; background:${sparkgaugeConfig.style.background}; width:100%;`">
113+
<!-- GUTTER -->
114+
<path
115+
:d="`M${10} ${svg.base} A 1 1 0 1 1 ${118} ${svg.base}`"
116+
:stroke="sparkgaugeConfig.style.gutter.color"
117+
:stroke-width="8"
118+
:stroke-linecap="sparkgaugeConfig.style.gutter.strokeLinecap"
119+
fill="none"
120+
/>
121+
<!-- TRACK -->
122+
<path
123+
v-if="valueRatio !== 0"
124+
:d="`M${10} ${svg.base} A 1 1 0 1 1 ${118} ${svg.base}`"
125+
:stroke="trackColor"
126+
:stroke-width="8"
127+
:stroke-linecap="sparkgaugeConfig.style.track.strokeLinecap"
128+
fill="none"
129+
:stroke-dasharray="169.5"
130+
:stroke-dashoffset="169.5 - (169.5 * valueRatio)"
131+
:class="{'vue-ui-sparkgauge-track' : sparkgaugeConfig.style.animation.show }"
132+
:style="sparkgaugeConfig.style.animation.show ? `animation: vue-ui-sparkgauge-animation ${sparkgaugeConfig.style.animation.speedMs}ms ease-in;`: ''"
133+
/>
134+
<!-- DATALABEL -->
135+
<text
136+
text-anchor="middle"
137+
:x="svg.width / 2"
138+
:y="svg.base + 6 + sparkgaugeConfig.style.dataLabel.offsetY"
139+
:font-size="sparkgaugeConfig.style.dataLabel.fontSize"
140+
:fill="labelColor"
141+
:font-weight="sparkgaugeConfig.style.dataLabel.bold ? 'bold' : 'normal'"
142+
>
143+
{{ dataLabel({ p: sparkgaugeConfig.style.dataLabel.prefix, v: currentScore, s: sparkgaugeConfig.style.dataLabel.suffix, r: sparkgaugeConfig.style.dataLabel.rounding }) }}
144+
</text>
145+
</svg>
146+
<!-- TITLE BOTTOM -->
147+
<div v-if="sparkgaugeConfig.style.title.show && nameLabel && sparkgaugeConfig.style.title.position === 'bottom'" class="vue-data-ui-sparkgauge-label" :style="`font-size:${sparkgaugeConfig.style.title.fontSize}px;text-align:${sparkgaugeConfig.style.title.textAlign};font-weight:${sparkgaugeConfig.style.title.bold ? 'bold': 'normal'};font-weight:${sparkgaugeConfig.style.title.bold ? 'bold': 'normal'}`">
148+
{{ nameLabel }}
149+
</div>
150+
</div>
151+
</template>
152+
153+
<style>
154+
@keyframes vue-ui-sparkgauge-animation {
155+
from {
156+
stroke-dashoffset: 169.5;
157+
opacity: -1;
158+
}
159+
}
160+
</style>

src/components/vue-ui-waffle.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ defineExpose({
563563
:x="position.x + waffleConfig.style.chart.layout.labels.captions.offsetX"
564564
:y="position.y + waffleConfig.style.chart.layout.labels.captions.offsetY"
565565
:height="absoluteRectDimension"
566-
:width="absoluteRectDimension * 3"
566+
:width="absoluteRectDimension * waffleConfig.style.chart.layout.grid.size"
567567
>
568568
<div class="vue-ui-waffle-caption" :style="`height: 100%; width: 100%; font-size:${waffleConfig.style.chart.layout.labels.captions.fontSize}px;display:flex;align-items:center;justify-content:flex-start;padding: 0 ${absoluteRectDimension / 12}px;color:${adaptColorToBackground(rects[i].color)};gap:2px`">
569569
<span v-if="waffleConfig.style.chart.layout.labels.captions.showSerieName">

src/default_configs.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3259,5 +3259,48 @@
32593259
"trackBlur": 1,
32603260
"trackColor": "#42d392"
32613261
}
3262+
},
3263+
"vue_ui_sparkgauge": {
3264+
"style": {
3265+
"fontFamily": "inherit",
3266+
"background": "#FFFFFF",
3267+
"height": 84,
3268+
"basePosition": 72,
3269+
"animation": {
3270+
"show": true,
3271+
"speedMs": 150
3272+
},
3273+
"title": {
3274+
"show": true,
3275+
"fontSize": 12,
3276+
"position": "top",
3277+
"textAlign": "center",
3278+
"bold": false,
3279+
"color": "#2D353C"
3280+
},
3281+
"dataLabel": {
3282+
"fontSize": 20,
3283+
"autoColor": true,
3284+
"color":"#2D353C",
3285+
"offsetY": 0,
3286+
"bold": true,
3287+
"rounding": 0,
3288+
"prefix": "",
3289+
"suffix": ""
3290+
},
3291+
"colors": {
3292+
"min": "#FF0000",
3293+
"max": "#00FF00"
3294+
},
3295+
"track": {
3296+
"autoColor": true,
3297+
"color": "#5f8bee",
3298+
"strokeLinecap": "round"
3299+
},
3300+
"gutter": {
3301+
"color": "#E1E5E8",
3302+
"strokeLinecap": "round"
3303+
}
3304+
}
32623305
}
32633306
}

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import Arrow from "./atoms/Arrow.vue"
3838
import VueUiMiniLoader from "./components/vue-ui-mini-loader.vue";
3939
import getVueDataUiConfig from "./getVueDataUiConfig";
4040
import VueUiNestedDonuts from "./components/vue-ui-nested-donuts.vue";
41+
import VueUiSparkgauge from './components/vue-ui-sparkgauge.vue';
4142

4243
export {
4344
VueUiXy,
@@ -79,5 +80,6 @@ export {
7980
Arrow,
8081
VueUiMiniLoader,
8182
getVueDataUiConfig,
82-
VueUiNestedDonuts
83+
VueUiNestedDonuts,
84+
VueUiSparkgauge
8385
};

src/main.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
VueUiTableSparkline,
4141
Arrow,
4242
VueUiMiniLoader,
43-
VueUiNestedDonuts
43+
VueUiNestedDonuts,
44+
VueUiSparkgauge
4445
} from 'vue-data-ui';
4546
import 'vue-data-ui/style.css';
4647

@@ -84,4 +85,5 @@ app.component("VueUiTableSparkline", VueUiTableSparkline);
8485
app.component("Arrow", Arrow);
8586
app.component("VueUiMiniLoader", VueUiMiniLoader);
8687
app.component("VueUiNestedDonuts", VueUiNestedDonuts);
88+
app.component("VueUiSparkgauge", VueUiSparkgauge);
8789
app.mount('#app');

0 commit comments

Comments
 (0)