Skip to content

Commit 6a9e4d4

Browse files
committed
VueUiKpi added component
1 parent 26ac927 commit 6a9e4d4

File tree

10 files changed

+250
-10
lines changed

10 files changed

+250
-10
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-40-green)
16+
![Static Badge](https://img.shields.io/badge/components-41-green)
1717

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

@@ -75,6 +75,7 @@ Available components:
7575
- [VueUiDashboard](https://vue-data-ui.graphieros.com/docs#vue-ui-dashboard)
7676
- [VueUiDigits](https://vue-data-ui.graphieros.com/docs#vue-ui-digits)
7777
- [VueUiIcon](https://vue-data-ui.graphieros.com/docs#vue-ui-icon)
78+
- [VueUiKpi](https://vue-data-ui.graphieros.com/docs#vue-ui-kpi)
7879
- [VueUiMiniLoader](https://vue-data-ui.graphieros.com/docs#vue-ui-mini-loader)
7980
- [VueUiScreenshot](https://vue-data-ui.graphieros.com/docs#vue-ui-screenshot)
8081
- [VueUiSkeleton](https://vue-data-ui.graphieros.com/docs#vue-ui-skeleton)

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: 2 additions & 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.51",
4+
"version": "2.0.52",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [
@@ -20,6 +20,7 @@
2020
"gauge",
2121
"graph",
2222
"heatmap",
23+
"kpi",
2324
"line",
2425
"molecule",
2526
"mood radar",

src/App.vue

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { getVueDataUiConfig } from "vue-data-ui";
5454
import SparkGaugeTest from "./components/vue-ui-sparkgauge.vue";
5555
import VueDataUiTest from "./components/vue-data-ui.vue";
5656
import VueDataUi from "./components/vue-data-ui.vue";
57+
import VueUiKpiTest from "./components/vue-ui-kpi.vue";
5758
import GalaxyTest from "./components/vue-ui-galaxy.vue";
5859
5960
const dataset = ref([
@@ -2855,6 +2856,34 @@ const galaxyConfig = ref({
28552856
}
28562857
})
28572858
2859+
const kpiDataset = ref(1000);
2860+
2861+
const kpiConfig = ref(
2862+
{
2863+
animationFrames: 60,
2864+
animationValueStart: 0,
2865+
backgroundColor: "#FFFFFF",
2866+
fontFamily: "inherit",
2867+
layoutClass: "",
2868+
layoutCss: "",
2869+
prefix: "",
2870+
suffix: "",
2871+
title: "Some kpi",
2872+
titleBold: true,
2873+
titleColor: "#CCCCCC",
2874+
titleClass: "",
2875+
titleCss: "",
2876+
titleFontSize: 16,
2877+
useAnimation: true,
2878+
valueBold: true,
2879+
valueColor: "#6376DD",
2880+
valueClass: "",
2881+
valueCss: "font-variant-numeric: tabular-nums;",
2882+
valueFontSize: 32,
2883+
valueRounding: 0
2884+
}
2885+
)
2886+
28582887
</script>
28592888

28602889
<template>
@@ -2959,6 +2988,44 @@ const galaxyConfig = ref({
29592988
</template>
29602989
</Box>
29612990

2991+
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_kpi)">
2992+
<template #title>
2993+
<!-- <BaseIcon name="chartGalaxy"/> -->
2994+
VueUiKpi
2995+
</template>
2996+
<template #info>
2997+
</template>
2998+
<template #dev>
2999+
<VueUiKpiTest :dataset="kpiDataset" :config="kpiConfig">
3000+
<!-- <template #comment-before="{ comment }">
3001+
{{ comment }}
3002+
</template>
3003+
<template #comment-after="{ comment }">
3004+
{{ comment }}
3005+
</template> -->
3006+
</VueUiKpiTest>
3007+
</template>
3008+
<template #prod>
3009+
<VueDataUi component="VueUiKpi" :dataset="kpiDataset" :config="kpiConfig">
3010+
<template #title="{ comment }">
3011+
TITLE SLOT {{ comment }}
3012+
</template>
3013+
<template #value="{ comment }">
3014+
VALUE SLOT {{ comment }}
3015+
</template>
3016+
<template #comment-before="{ comment }">
3017+
COMMENT BEFORE SLOT {{ comment }}
3018+
</template>
3019+
<template #comment-after="{ comment }">
3020+
COMMENT AFTER SLOT {{ comment }}
3021+
</template>
3022+
</VueDataUi>
3023+
</template>
3024+
<template #config>
3025+
{{ PROD_CONFIG.vue_ui_kpi }}
3026+
</template>
3027+
</Box>
3028+
29623029
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_galaxy)">
29633030
<template #title>
29643031
<BaseIcon name="chartGalaxy"/>

src/components/vue-data-ui.vue

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import VueUiDonutEvolution from "./vue-ui-donut-evolution.vue";
1212
import VueUiDonut from "./vue-ui-donut.vue";
1313
import VueUiGalaxy from './vue-ui-galaxy.vue';
1414
import VueUiGauge from "./vue-ui-gauge.vue";
15+
import VueUiKpi from "./vue-ui-kpi.vue";
1516
import VueUiHeatmap from "./vue-ui-heatmap.vue";
1617
import VueUiMiniLoader from "./vue-ui-mini-loader.vue";
1718
import VueUiMolecule from "./vue-ui-molecule.vue";
@@ -49,7 +50,7 @@ const props = defineProps({
4950
})
5051
5152
const isError = computed(() => {
52-
return !["VueUi3dBar", "VueUiAgePyramid", "VueUiAnnotator", "VueUiCandlestick", "VueUiChestnut", "VueUiDashboard", "VueUiDigits", "VueUiDonutEvolution", "VueUiDonut", "VueUiGauge", "VueUiGalaxy", "VueUiHeatmap", "VueUiMiniLoader", "VueUiMolecule", "VueUiMoodRadar", "VueUiNestedDonuts", "VueUiOnion", "VueUiQuadrant", "VueUiRadar", "VueUiRating", "VueUiRelationCircle", "VueUiRings", "VueUiScatter", "VueUiScreenshot", "VueUiSkeleton", "VueUiSmiley", "VueUiSparkbar", "VueUiSparkgauge", "VueUiSparkHistogram", "VueUiSparkline", "VueUiSparkStackbar", "VueUiTableSparkline", "VueUiTable", "VueUiThermometer", "VueUiTiremarks", "VueUiVerticalBar", "VueUiWaffle", "VueUiWheel", "VueUiXy"].includes(props.component)
53+
return !["VueUi3dBar", "VueUiAgePyramid", "VueUiAnnotator", "VueUiCandlestick", "VueUiChestnut", "VueUiDashboard", "VueUiDigits", "VueUiDonutEvolution", "VueUiDonut", "VueUiGauge", "VueUiGalaxy", "VueUiHeatmap", "VueUiKpi", "VueUiMiniLoader", "VueUiMolecule", "VueUiMoodRadar", "VueUiNestedDonuts", "VueUiOnion", "VueUiQuadrant", "VueUiRadar", "VueUiRating", "VueUiRelationCircle", "VueUiRings", "VueUiScatter", "VueUiScreenshot", "VueUiSkeleton", "VueUiSmiley", "VueUiSparkbar", "VueUiSparkgauge", "VueUiSparkHistogram", "VueUiSparkline", "VueUiSparkStackbar", "VueUiTableSparkline", "VueUiTable", "VueUiThermometer", "VueUiTiremarks", "VueUiVerticalBar", "VueUiWaffle", "VueUiWheel", "VueUiXy"].includes(props.component)
5354
});
5455
5556
const vue_ui_3d_bar = ref(null);
@@ -91,6 +92,7 @@ const vue_ui_vertical_bar = ref(null);
9192
const vue_ui_waffle = ref(null);
9293
const vue_ui_wheel = ref(null);
9394
const vue_ui_xy = ref(null);
95+
const vue_ui_kpi = ref(null);
9496
9597
const emit = defineEmits([
9698
'selectLegend',
@@ -122,7 +124,7 @@ const recalculateHeight = ref(() => null);
122124
123125
onMounted(() => {
124126
if (isError.value) {
125-
throw new Error(`\n\nVue Data UI exception:\nThe provided component "${props.component}" does not exist. Check the spelling.\n\nAvailable components:\n\n. VueUi3dBar\n. VueUiAgePyramid\n. VueUiAnnotator\n. VueUiCandlestick\n. VueUiChestnut\n. VueUiDashboard\n. VueUiDigits\n. VueUiDonutEvolution\n. VueUiDonut\n. VueUiGauge\n. VueUiHeatmap\n. VueUiMiniLoadar\n. VueUiMolecule\n. VueUiMoodRadar\n. VueUiNestedDonuts\n. VueUiOnion\n. VueUiQuadrant\n. VueUiRadar\n. VueUiRating\n. VueUiRelationCircle\n. VueUiRings\n. VueUiScatter\n. VueUiScreenshot\n. VueUiSkeleton\n. VueUiSmiley\n. VueUiSparkbar\n. VueUiSparkgauge\n. VueUiSparkHistogram\n. VueUiSparkline\n. VueUiSparkStackbar\n. VueUiTableSparkline\n. VueUiTable\n. VueUiThermometer\n. VueUiTiremarks\n. VueUiVerticalBar\n. VueUiWaffle\n. VueUiWheel\n. VueUiXy\n\n`)
127+
throw new Error(`\n\nVue Data UI exception:\nThe provided component "${props.component}" does not exist. Check the spelling.\n\nAvailable components:\n\n. VueUi3dBar\n. VueUiAgePyramid\n. VueUiAnnotator\n. VueUiCandlestick\n. VueUiChestnut\n. VueUiDashboard\n. VueUiDigits\n. VueUiDonutEvolution\n. VueUiDonut\n. VueUiGauge\n. VueUiHeatmap\n. VueUiMiniLoadar\n. VueUiKpi\n. VueUiMolecule\n. VueUiMoodRadar\n. VueUiNestedDonuts\n. VueUiOnion\n. VueUiQuadrant\n. VueUiRadar\n. VueUiRating\n. VueUiRelationCircle\n. VueUiRings\n. VueUiScatter\n. VueUiScreenshot\n. VueUiSkeleton\n. VueUiSmiley\n. VueUiSparkbar\n. VueUiSparkgauge\n. VueUiSparkHistogram\n. VueUiSparkline\n. VueUiSparkStackbar\n. VueUiTableSparkline\n. VueUiTable\n. VueUiThermometer\n. VueUiTiremarks\n. VueUiVerticalBar\n. VueUiWaffle\n. VueUiWheel\n. VueUiXy\n\n`)
126128
}
127129
128130
if(vue_ui_3d_bar.value) {
@@ -485,6 +487,27 @@ defineExpose({
485487
<slot name="svg" :svg="svg"></slot>
486488
</template>
487489
</VueUiHeatmap>
490+
491+
<VueUiKpi
492+
v-if="component === 'VueUiKpi'"
493+
:config="config"
494+
:dataset="dataset"
495+
ref="vue_ui_kpi"
496+
>
497+
<template #title="{ comment }">
498+
<slot name="title" :comment="comment"></slot>
499+
</template>
500+
<template #value="{ comment }">
501+
<slot name="value" :comment="comment"></slot>
502+
</template>
503+
<template #comment-before="{ comment }">
504+
<slot name="comment-before" :comment="comment"></slot>
505+
</template>
506+
<template #comment-after="{ comment }">
507+
<slot name="comment-after" :comment="comment"></slot>
508+
</template>
509+
510+
</VueUiKpi>
488511

489512
<VueUiMiniLoader
490513
v-if="component === 'VueUiMiniLoader'"

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import VueUiKpi from "./vue-ui-kpi.vue";
2+
3+
describe('<VueUiKpi />', () => {
4+
it('renders', () => {
5+
cy.mount(VueUiKpi, {
6+
props: {
7+
dataset: 1000
8+
},
9+
slots: {
10+
title: () => "TITLE SLOT",
11+
value: () => "VALUE SLOT",
12+
['comment-before']: () => "COMMENT BEFORE SLOT",
13+
['comment-after']: () => "COMMENT AFTER SLOT"
14+
}
15+
})
16+
17+
cy.get('.vue-ui-kpi')
18+
.as('container')
19+
.should('contain', 'TITLE SLOT')
20+
.and('contain', 'VALUE SLOT')
21+
.and('contain', 'COMMENT BEFORE SLOT')
22+
.and('contain', 'COMMENT AFTER SLOT')
23+
24+
cy.get('@container').should('contain', 0)
25+
cy.wait(1000)
26+
cy.get('@container').should('contain', 1000)
27+
})
28+
})

src/components/vue-ui-kpi.vue

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<script setup>
2+
import { ref, computed, onMounted } from "vue";
3+
import mainConfig from "../default_configs.json";
4+
import { useNestedProp } from "../useNestedProp";
5+
import { dataLabel } from "../lib";
6+
7+
const props = defineProps({
8+
config: {
9+
type: Object,
10+
default() {
11+
return {}
12+
}
13+
},
14+
dataset: {
15+
type: Number,
16+
default: 0
17+
},
18+
});
19+
20+
const defaultConfig = ref(mainConfig.vue_ui_kpi);
21+
22+
const kpiConfig = computed(() => {
23+
return useNestedProp({
24+
userConfig: props.config,
25+
defaultConfig: defaultConfig.value
26+
})
27+
});
28+
29+
const formattedValue = ref(typeof props.dataset === 'number' ? props.dataset : props.dataset);
30+
const displayedValue = ref(kpiConfig.value.useAnimation ? kpiConfig.value.animationValueStart : formattedValue.value );
31+
32+
onMounted(() => {
33+
const chunks = kpiConfig.value.animationFrames;
34+
const chunk = props.dataset / chunks;
35+
36+
function animate() {
37+
displayedValue.value += chunk;
38+
if (displayedValue.value < props.dataset) {
39+
requestAnimationFrame(animate)
40+
} else {
41+
displayedValue.value = props.dataset;
42+
}
43+
}
44+
45+
if (kpiConfig.value.useAnimation) {
46+
displayedValue.value = 0;
47+
animate()
48+
}
49+
});
50+
51+
</script>
52+
53+
<template>
54+
<div :class="`vue-ui-kpi ${kpiConfig.layoutClass}`" :style="`${kpiConfig.layoutCss}`">
55+
<div :class="`vue-ui-kpi-title ${kpiConfig.titleClass}`" :style="`font-family: ${kpiConfig.fontFamily}; font-size:${kpiConfig.titleFontSize}px; color:${kpiConfig.titleColor}; font-weight:${kpiConfig.titleBold ? 'bold' : 'normal'}; ${kpiConfig.titleCss}`">
56+
<slot name="title" :comment="dataset"></slot>
57+
{{ kpiConfig.title }}
58+
</div>
59+
<slot name="comment-before" :comment="dataset"></slot>
60+
<div :class="`vue-ui-kpi-value ${kpiConfig.valueClass}`" :style="`font-family: ${kpiConfig.fontFamily}; font-size:${kpiConfig.valueFontSize}px; color:${kpiConfig.valueColor}; font-weight:${kpiConfig.valueBold ? 'bold': 'normal'}; ${kpiConfig.valueCss}`">
61+
<slot name="value" :comment="dataset"></slot>
62+
{{ dataLabel({ p: kpiConfig.prefix, v: displayedValue, s: kpiConfig.suffix, r: kpiConfig.valueRounding }) }}
63+
</div>
64+
<slot name="comment-after" :comment="dataset"></slot>
65+
</div>
66+
</template>

src/default_configs.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3393,5 +3393,28 @@
33933393
"userOptions": {
33943394
"show": true
33953395
}
3396+
},
3397+
"vue_ui_kpi": {
3398+
"animationFrames": 60,
3399+
"animationValueStart": 0,
3400+
"backgroundColor": "#FFFFFF",
3401+
"fontFamily": "inherit",
3402+
"layoutClass": "",
3403+
"layoutCss": "",
3404+
"prefix": "",
3405+
"suffix": "",
3406+
"title": "",
3407+
"titleBold": true,
3408+
"titleColor": "#2D353C",
3409+
"titleClass": "",
3410+
"titleCss": "",
3411+
"titleFontSize": 16,
3412+
"useAnimation": true,
3413+
"valueBold": true,
3414+
"valueColor": "#6376DD",
3415+
"valueClass": "",
3416+
"valueCss": "",
3417+
"valueFontSize": 32,
3418+
"valueRounding": 0
33963419
}
33973420
}

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import VueUiNestedDonuts from "./components/vue-ui-nested-donuts.vue";
4141
import VueUiSparkgauge from './components/vue-ui-sparkgauge.vue';
4242
import VueDataUi from "./components/vue-data-ui.vue";
4343
import VueUiGalaxy from "./components/vue-ui-galaxy.vue";
44+
import VueUiKpi from "./components/vue-ui-kpi.vue";
4445

4546
export {
4647
VueUiXy,
@@ -85,5 +86,6 @@ export {
8586
VueUiNestedDonuts,
8687
VueUiSparkgauge,
8788
VueDataUi,
88-
VueUiGalaxy
89+
VueUiGalaxy,
90+
VueUiKpi
8991
};

0 commit comments

Comments
 (0)