Skip to content

Commit 92ea61c

Browse files
committed
VueUiQuickChart added new component
1 parent e14d06a commit 92ea61c

File tree

15 files changed

+2016
-36
lines changed

15 files changed

+2016
-36
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-44-green)
16+
![Static Badge](https://img.shields.io/badge/components-45-green)
1717

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

@@ -36,6 +36,7 @@ Available components:
3636
- [VueUiNestedDonuts](https://vue-data-ui.graphieros.com/docs#vue-ui-nested-donuts)
3737
- [VueUiOnion](https://vue-data-ui.graphieros.com/docs#vue-ui-onion)
3838
- [VueUiQuadrant](https://vue-data-ui.graphieros.com/docs#vue-ui-quadrant)
39+
- [VueUiQuickChart](https://vue-data-ui.graphieros.com/docs#vue-ui-quick-chart)
3940
- [VueUiRadar](https://vue-data-ui.graphieros.com/docs#vue-ui-radar)
4041
- [VueUiRelationCircle](https://vue-data-ui.graphieros.com/docs#vue-ui-relation-circle)
4142
- [VueUiRings](https://vue-data-ui.graphieros.com/docs#vue-ui-rings)

package-lock.json

Lines changed: 3 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.1.2",
4+
"version": "2.1.3",
55
"type": "module",
66
"description": "A user-empowering data visualization Vue components library",
77
"keywords": [

src/App.vue

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import GalaxyTest from "./components/vue-ui-galaxy.vue";
5959
import TreemapTest from "./components/vue-ui-treemap.vue";
6060
import TableHeatmapTest from "./components/vue-ui-table-heatmap.vue";
6161
import AccordionTest from "./components/vue-ui-accordion.vue";
62+
import QuickTest from "./components/vue-ui-quick-chart.vue";
6263
6364
const dataset = ref([
6465
{
@@ -2635,9 +2636,12 @@ const donutConfig = ref({
26352636
style: {
26362637
chart: {
26372638
layout: {
2639+
donut: {
2640+
strokeWidth: 130
2641+
},
26382642
labels: {
26392643
dataLabels: {
2640-
useLabelSlots: true
2644+
useLabelSlots: false
26412645
}
26422646
}
26432647
},
@@ -3258,6 +3262,68 @@ const tableHeatmapDataset = ref([
32583262
}
32593263
])
32603264
3265+
const quickDatasetDonut = ref([
3266+
{
3267+
name: 'serie1',
3268+
value: 10,
3269+
},
3270+
{
3271+
name: 'Serie 2',
3272+
value: 20,
3273+
},
3274+
{
3275+
name: 'Serie 3',
3276+
value: 5,
3277+
},
3278+
{
3279+
name: 'Serie 4',
3280+
value: 2,
3281+
},
3282+
{
3283+
name: 'Serie 5',
3284+
value: 1,
3285+
},
3286+
]);
3287+
3288+
const quickDatasetLine = ref([
3289+
{
3290+
name: 'Serie1',
3291+
values: [1, 2, 3, 2, 3, 4, 3, 4, 5]
3292+
},
3293+
{
3294+
name: 'Serie2',
3295+
values: [5, 6, 7, 6, 7, 8, 7, 8, 9]
3296+
},
3297+
{
3298+
name: 'Serie3',
3299+
values: [1, 2, 3, 5, 8, 13, 21, 13, 8]
3300+
},
3301+
])
3302+
3303+
3304+
const quickDatasetSimpleLine = ref([1, 2, 3, 5, 8, 13, 21, 34, 55, 89])
3305+
const quickDatasetSimpleBar = ref([1, 2, -3, 5, 8])
3306+
3307+
const quickDatasetBar = ref([
3308+
{
3309+
name: 'Serie1',
3310+
values: [1, 2, 3, 2, 3]
3311+
},
3312+
{
3313+
name: 'Serie2',
3314+
values: [5, 6, 7, 6, 7]
3315+
},
3316+
{
3317+
name: 'Serie3',
3318+
values: [1, 2, 3, 5, 8,]
3319+
},
3320+
])
3321+
3322+
const quickConfig = ref({
3323+
title: 'Some title that can be kinda long',
3324+
xyPeriods: ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']
3325+
})
3326+
32613327
</script>
32623328

32633329
<template>
@@ -3398,6 +3464,32 @@ const tableHeatmapDataset = ref([
33983464
</template>
33993465
</Box>
34003466

3467+
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_quick_chart)">
3468+
<template #title>
3469+
<BaseIcon name="vueDataUi"/>
3470+
VueUiQuickChart
3471+
</template>
3472+
<template #info>
3473+
</template>
3474+
<template #dev>
3475+
<QuickTest :dataset="quickDatasetDonut" :config="quickConfig"/>
3476+
<QuickTest :dataset="quickDatasetLine" :config="quickConfig"/>
3477+
<QuickTest :dataset="quickDatasetSimpleLine" :config="quickConfig"/>
3478+
<QuickTest :dataset="quickDatasetBar" :config="quickConfig"/>
3479+
<QuickTest :dataset="quickDatasetSimpleBar" :config="quickConfig"/>
3480+
</template>
3481+
<template #prod>
3482+
<VueDataUi component="VueUiQuickChart" :dataset="quickDatasetDonut" :config="quickConfig"/>
3483+
<VueUiQuickChart :dataset="quickDatasetLine" :config="quickConfig"/>
3484+
<VueUiQuickChart :dataset="quickDatasetSimpleLine" :config="quickConfig"/>
3485+
<VueUiQuickChart :dataset="quickDatasetBar" :config="quickConfig"/>
3486+
<VueUiQuickChart :dataset="quickDatasetSimpleBar" :config="quickConfig"/>
3487+
</template>
3488+
<template #config>
3489+
{{ PROD_CONFIG.vue_ui_quick_chart }}
3490+
</template>
3491+
</Box>
3492+
34013493
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_accordion)">
34023494
<template #title>
34033495
<BaseIcon name="accordion"/>
@@ -3445,7 +3537,7 @@ const tableHeatmapDataset = ref([
34453537
</template>
34463538
</Box>
34473539

3448-
<Box open @copy="copyConfig(PROD_CONFIG.vue_ui_table_heatmap)">
3540+
<Box @copy="copyConfig(PROD_CONFIG.vue_ui_table_heatmap)">
34493541
<template #title>
34503542
<BaseIcon name="chartTable"/>
34513543
VueUiTableHeatmap

src/chartDetector.js

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
export const chartType = {
2+
LINE: 'LINE',
3+
BAR: 'BAR',
4+
DONUT: 'DONUT',
5+
};
6+
7+
// TODO: find a way to make this list extensible in config
8+
export const nameType = ['NAME', 'TITLE', 'DESCRIPTION', 'LABEL'];
9+
export const dataType = ['SERIE', 'SERIES', 'DATA', 'VALUE', 'VALUES', 'NUM'];
10+
export const timeType = ['TIME', 'PERIOD', 'MONTH', 'YEAR', 'MONTHS', 'YEARS', 'DAY', 'DAYS', 'HOUR', 'HOURS']
11+
12+
export function detectChart({dataset, barLineSwitch = 6}) {
13+
let type = null;
14+
let usableDataset = null;
15+
16+
const isJustANumber = typeof dataset === 'number';
17+
const isJustAString = typeof dataset === 'string';
18+
19+
if (isJustANumber || isJustAString) {
20+
console.warn(`The provided dataset (${dataset}) is not sufficient to build a chart`);
21+
}
22+
23+
if (isSimpleArray(dataset)) {
24+
25+
if (isSimpleArrayOfNumbers(dataset)) {
26+
dataset.length < barLineSwitch ? type = chartType.BAR : type = chartType.LINE;
27+
usableDataset = dataset;
28+
}
29+
30+
if (isSimpleArrayOfObjects(dataset)) {
31+
if (!isArrayOfObjectsOfSameDataType(dataset)) {
32+
console.warn('The objects in the dataset array have a different data structure. Either keys or value types are different.')
33+
return false
34+
}
35+
const keys = Object.keys(dataset[0]);
36+
const values = Object.values(dataset[0]);
37+
if (!keys.some(key => hasValidDataTypeKey(key))) {
38+
console.warn('The data type of the dataset objects in the array must contain one of the following keys: DATA, SERIES, VALUE, VALUES, NUM. Casing is not important.')
39+
return false;
40+
}
41+
42+
if (passesDatatypeCheck(values, (v) => {
43+
return typeof v === 'number'
44+
})) {
45+
type = chartType.DONUT;
46+
usableDataset = dataset;
47+
}
48+
49+
if (passesDatatypeCheck(values, (v) => {
50+
return Array.isArray(v) && isSimpleArrayOfNumbers(v)
51+
})) {
52+
if (maxLengthOfArrayTypesInArrayOfObjects(dataset) > barLineSwitch) {
53+
type = chartType.LINE
54+
} else {
55+
type = chartType.BAR
56+
}
57+
usableDataset = dataset.map(d => {
58+
return {
59+
...d,
60+
data: getFirstEntryMatch(d, (v) => isSimpleArrayOfNumbers(v))
61+
}
62+
})
63+
}
64+
dataset = dataset.map(d => uppercaseKeys(d))
65+
usableDataset = usableDataset.map(d => uppercaseKeys(d))
66+
}
67+
}
68+
69+
// IS MATRIX
70+
const isMatrix = !isSimpleArray(dataset) ? false : [...new Set(dataset.flatMap(d => Array.isArray(d)))][0];
71+
72+
return {
73+
dataset,
74+
type,
75+
usableDataset
76+
}
77+
}
78+
79+
80+
// UTILS
81+
82+
export function isEmptyDataset(d) {
83+
return !d || (isSimpleArray(d) && !d.length);
84+
}
85+
86+
export function isSimpleArray(d) {
87+
return Array.isArray(d);
88+
}
89+
90+
export function isEmptyObject(d) {
91+
return !isSimpleArray(d) && typeof d === 'object' && Object.keys(d).length > 0;
92+
}
93+
94+
export function isSimpleArrayOfNumbers(d) {
95+
if (!isSimpleArray(d) || isEmptyDataset(d)) return false;
96+
const converted = d.map(v => Number(v));
97+
return ![...new Set(converted.flatMap(d => typeof d === 'number' && !isNaN(d)))].includes(false);
98+
}
99+
100+
export function isSimpleArrayOfStrings(d) {
101+
if (!isSimpleArray(d) || isEmptyDataset(d)) return false;
102+
return ![...new Set(d.flatMap(d => typeof d === 'string'))].includes(false);
103+
}
104+
105+
export function isSimpleArrayOfObjects(d) {
106+
if (!isSimpleArray(d) || isEmptyDataset(d)) return false;
107+
const isArrayOfObjects = ![...new Set(d.flatMap(v => typeof v === 'object' && !Array.isArray(v)))].includes(false);
108+
if(!isArrayOfObjects) return false;
109+
return !d.map(v => Object.keys(v).length > 0).includes(false)
110+
}
111+
112+
export function haveSameStructure(obj1, obj2) {
113+
const keys1 = Object.keys(obj1).sort();
114+
const keys2 = Object.keys(obj2).sort();
115+
if (keys1.length !== keys2.length) {
116+
return false;
117+
}
118+
for (let i = 0; i < keys1.length; i += 1) {
119+
const key1 = keys1[i];
120+
const key2 = keys2[i];
121+
122+
if (key1 !== key2 || typeof obj1[key1] !== typeof obj2[key2]) {
123+
return false;
124+
}
125+
}
126+
return true;
127+
}
128+
129+
export function isArrayOfObjectsOfSameDataType(d) {
130+
if (d.length <= 1) return true;
131+
for (let i = 0; i < d.length; i += 1) {
132+
for (let j = i + 1; j < d.length; j += 1) {
133+
if (!haveSameStructure(d[i], d[j])) {
134+
return false;
135+
}
136+
}
137+
}
138+
return true;
139+
}
140+
141+
export function hasValidDataTypeKey(key) {
142+
return dataType.includes(key.toUpperCase())
143+
}
144+
145+
export function passesDatatypeCheck(datapoints, checkTypeFunction) {
146+
let arr = [];
147+
148+
for (let i = 0; i < datapoints.length; i += 1) {
149+
arr.push(checkTypeFunction(datapoints[i]))
150+
}
151+
return arr.includes(true);
152+
}
153+
154+
export function maxLengthOfArrayTypesInArrayOfObjects(ds) {
155+
return Math.max(...[...ds].flatMap(d => {
156+
return Object.values(d).filter(d => isSimpleArrayOfNumbers(d)).map(d => d.length)
157+
}))
158+
}
159+
160+
export function getFirstEntryMatch(datapoint, matchFunction) {
161+
return Object.values(datapoint).filter(d => matchFunction(d))[0]
162+
}
163+
164+
export function uppercaseKeys(obj) {
165+
const newObj = {};
166+
for (let key in obj) {
167+
if (obj.hasOwnProperty(key)) {
168+
newObj[key.toUpperCase()] = obj[key];
169+
}
170+
}
171+
return newObj;
172+
}
173+
174+
const chartDetector = {
175+
dataType,
176+
detectChart,
177+
getFirstEntryMatch,
178+
hasValidDataTypeKey,
179+
isArrayOfObjectsOfSameDataType,
180+
isEmptyDataset,
181+
isEmptyObject,
182+
isSimpleArray,
183+
isSimpleArrayOfNumbers,
184+
isSimpleArrayOfObjects,
185+
isSimpleArrayOfStrings,
186+
maxLengthOfArrayTypesInArrayOfObjects,
187+
nameType,
188+
passesDatatypeCheck,
189+
timeType,
190+
uppercaseKeys,
191+
}
192+
193+
export default chartDetector;

0 commit comments

Comments
 (0)