@flux-ui/statistics 3.1.12 → 3.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +30 -1
- package/dist/index.js +5149 -4926
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/component/FluxStatisticsDetailsTable.vue +7 -2
- package/src/component/FluxStatisticsDetailsTableRow.vue +9 -3
- package/src/component/FluxStatisticsLegend.vue +15 -3
- package/src/component/FluxStatisticsLegendItem.vue +6 -13
- package/src/component/FluxStatisticsPercentageBar.vue +18 -13
- package/src/css/Legend.module.scss +22 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flux-ui/statistics",
|
|
3
3
|
"description": "Statistics components for the Flux UI library.",
|
|
4
|
-
"version": "3.1
|
|
4
|
+
"version": "3.2.0-beta.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/basmilius",
|
|
@@ -51,21 +51,21 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@basmilius/common": "^3.45.0",
|
|
53
53
|
"@basmilius/utils": "^3.45.0",
|
|
54
|
-
"@flux-ui/components": "3.1
|
|
55
|
-
"@flux-ui/internals": "3.1
|
|
56
|
-
"@flux-ui/types": "3.1
|
|
54
|
+
"@flux-ui/components": "3.2.0-beta.1",
|
|
55
|
+
"@flux-ui/internals": "3.2.0-beta.1",
|
|
56
|
+
"@flux-ui/types": "3.2.0-beta.1",
|
|
57
57
|
"clsx": "^2.1.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"echarts": "^6.1.0",
|
|
61
61
|
"lodash-es": "^4.18.1",
|
|
62
|
-
"vue": "^3.6.0-beta.
|
|
62
|
+
"vue": "^3.6.0-beta.17",
|
|
63
63
|
"vue-i18n": "^11.4.6"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@basmilius/vite-preset": "^3.45.0",
|
|
67
67
|
"@types/lodash-es": "^4.17.12",
|
|
68
|
-
"@types/node": "^26.0.
|
|
68
|
+
"@types/node": "^26.0.1",
|
|
69
69
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
70
70
|
"@vue/tsconfig": "^0.9.1",
|
|
71
71
|
"sass-embedded": "^1.100.0",
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
role="table"
|
|
4
|
+
:aria-label="title"
|
|
5
|
+
:class="$style.detailsTable">
|
|
3
6
|
<div :class="$style.detailsTableTitle">
|
|
4
7
|
{{ title }}
|
|
5
8
|
</div>
|
|
6
9
|
|
|
7
|
-
<div
|
|
10
|
+
<div
|
|
11
|
+
role="rowgroup"
|
|
12
|
+
:class="$style.detailsTableRows">
|
|
8
13
|
<slot/>
|
|
9
14
|
</div>
|
|
10
15
|
</div>
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
2
|
+
<div
|
|
3
|
+
role="row"
|
|
4
|
+
:class="$style.detailsTableRow">
|
|
5
|
+
<span
|
|
6
|
+
role="rowheader"
|
|
7
|
+
:class="$style.detailsTableRowLabel">
|
|
4
8
|
{{ label }}
|
|
5
9
|
</span>
|
|
6
10
|
|
|
7
|
-
<span
|
|
11
|
+
<span
|
|
12
|
+
role="cell"
|
|
13
|
+
:class="$style.detailsTableRowValue">
|
|
8
14
|
{{ value }}
|
|
9
15
|
</span>
|
|
10
16
|
</div>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
ref="list"
|
|
4
|
+
role="list"
|
|
5
|
+
:class="containerClass">
|
|
3
6
|
<slot v-if="hasSlot"/>
|
|
4
7
|
<FluxStatisticsLegendItem
|
|
5
8
|
v-else
|
|
@@ -11,16 +14,19 @@
|
|
|
11
14
|
:label="item.label"
|
|
12
15
|
:value="item.value"
|
|
13
16
|
@mouseenter="onItemMouseEnter(index)"
|
|
14
|
-
@mouseleave="onItemMouseLeave"
|
|
17
|
+
@mouseleave="onItemMouseLeave"
|
|
18
|
+
@focus="onItemMouseEnter(index)"
|
|
19
|
+
@blur="onItemMouseLeave"/>
|
|
15
20
|
</div>
|
|
16
21
|
</template>
|
|
17
22
|
|
|
18
23
|
<script
|
|
19
24
|
lang="ts"
|
|
20
25
|
setup>
|
|
26
|
+
import { useFocusZone } from '@flux-ui/internals';
|
|
21
27
|
import type { FluxDirection } from '@flux-ui/types';
|
|
22
28
|
import { clsx } from 'clsx';
|
|
23
|
-
import { computed, inject, provide, toRef, useSlots } from 'vue';
|
|
29
|
+
import { computed, inject, provide, toRef, useSlots, useTemplateRef } from 'vue';
|
|
24
30
|
import { FluxStatisticsChartLegendInjectionKey, type FluxStatisticsLegendVariant, FluxStatisticsLegendVariantInjectionKey } from '~flux/statistics/composable';
|
|
25
31
|
import FluxStatisticsLegendItem from './FluxStatisticsLegendItem.vue';
|
|
26
32
|
import $style from '~flux/statistics/css/Legend.module.scss';
|
|
@@ -35,6 +41,12 @@
|
|
|
35
41
|
|
|
36
42
|
const slots = useSlots();
|
|
37
43
|
const legendContext = inject(FluxStatisticsChartLegendInjectionKey, null);
|
|
44
|
+
const listRef = useTemplateRef('list');
|
|
45
|
+
|
|
46
|
+
// One tab stop for the whole legend; arrow keys move focus between items. Bidirectional handles
|
|
47
|
+
// every layout (detailed column, compact row/wrap/column) since it resolves the next item
|
|
48
|
+
// geometrically. Focusing an item drives the same hover sync as the mouse.
|
|
49
|
+
useFocusZone(listRef, {direction: 'bidirectional'});
|
|
38
50
|
|
|
39
51
|
const hasSlot = computed(() => !!slots.default);
|
|
40
52
|
const autoItems = computed(() => legendContext?.items.value ?? []);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<button
|
|
3
|
+
type="button"
|
|
4
|
+
role="listitem"
|
|
3
5
|
:class="clsx(
|
|
4
6
|
variant === 'compact' ? $style.statisticsLegendItemCompact : $style.statisticsLegendItem,
|
|
5
7
|
variant !== 'compact' && isHovered && $style.isHovered
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
:class="$style.statisticsLegendItemValue">
|
|
29
31
|
{{ value }}
|
|
30
32
|
</span>
|
|
31
|
-
</
|
|
33
|
+
</button>
|
|
32
34
|
</template>
|
|
33
35
|
|
|
34
36
|
<script
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
import { clsx } from 'clsx';
|
|
40
42
|
import { computed, inject } from 'vue';
|
|
41
43
|
import { FluxStatisticsLegendVariantInjectionKey } from '~flux/statistics/composable';
|
|
44
|
+
import { resolveChartColor } from '~flux/statistics/util';
|
|
42
45
|
import $style from '~flux/statistics/css/Legend.module.scss';
|
|
43
46
|
|
|
44
47
|
const {
|
|
@@ -54,15 +57,5 @@
|
|
|
54
57
|
const variantRef = inject(FluxStatisticsLegendVariantInjectionKey, null);
|
|
55
58
|
const variant = computed(() => variantRef?.value ?? 'detailed');
|
|
56
59
|
|
|
57
|
-
const colorValue = computed(() =>
|
|
58
|
-
if (!color) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (['gray', 'primary', 'danger', 'info', 'success', 'warning'].includes(color)) {
|
|
63
|
-
return `var(--${color}-600)`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return color;
|
|
67
|
-
});
|
|
60
|
+
const colorValue = computed(() => resolveChartColor(color));
|
|
68
61
|
</script>
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
role="img"
|
|
4
|
+
:aria-label="ariaLabel"
|
|
5
|
+
:class="$style.statisticsPercentageBar">
|
|
3
6
|
<div :class="clsx($style.statisticsPercentageBarTrack, hoveredIndex !== null && $style.isHoverActive)">
|
|
4
7
|
<FluxTooltip
|
|
5
|
-
v-for="(item, index) of
|
|
8
|
+
v-for="(item, index) of normalizedItems"
|
|
6
9
|
:key="index">
|
|
7
10
|
<template #content>
|
|
8
11
|
<div :class="$style.statisticsPercentageBarTooltip">
|
|
@@ -37,16 +40,22 @@
|
|
|
37
40
|
import { clsx } from 'clsx';
|
|
38
41
|
import { computed, inject, watchEffect } from 'vue';
|
|
39
42
|
import { type ChartLegendItem, FluxStatisticsChartLegendInjectionKey } from '~flux/statistics/composable';
|
|
43
|
+
import { resolveChartColor } from '~flux/statistics/util';
|
|
40
44
|
import $style from '~flux/statistics/css/PercentageBar.module.scss';
|
|
41
45
|
|
|
42
|
-
const SEMANTIC_COLORS = ['gray', 'primary', 'danger', 'info', 'success', 'warning'] as const;
|
|
43
|
-
|
|
44
46
|
const props = defineProps<{
|
|
45
47
|
readonly items: FluxStatisticsPercentageBarItemObject[];
|
|
46
48
|
}>();
|
|
47
49
|
|
|
48
50
|
const legendContext = inject(FluxStatisticsChartLegendInjectionKey, null);
|
|
49
51
|
|
|
52
|
+
const normalizedItems = computed(() =>
|
|
53
|
+
props.items.map(item => ({
|
|
54
|
+
...item,
|
|
55
|
+
value: Math.max(0, item.value)
|
|
56
|
+
}))
|
|
57
|
+
);
|
|
58
|
+
|
|
50
59
|
const legendItems = computed<ChartLegendItem[]>(() =>
|
|
51
60
|
props.items.map(item => ({
|
|
52
61
|
color: resolveColor(item.color),
|
|
@@ -56,6 +65,10 @@
|
|
|
56
65
|
}))
|
|
57
66
|
);
|
|
58
67
|
|
|
68
|
+
const ariaLabel = computed(() =>
|
|
69
|
+
props.items.map(item => `${formatPercentage(Math.max(0, item.value))} ${item.label}`).join(', ')
|
|
70
|
+
);
|
|
71
|
+
|
|
59
72
|
const hoveredIndex = computed<number | null>(() => legendContext?.hoveredIndex.value ?? null);
|
|
60
73
|
|
|
61
74
|
watchEffect(() => {
|
|
@@ -65,15 +78,7 @@
|
|
|
65
78
|
});
|
|
66
79
|
|
|
67
80
|
function resolveColor(color?: FluxStatisticsChartColor): string | undefined {
|
|
68
|
-
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (SEMANTIC_COLORS.includes(color as typeof SEMANTIC_COLORS[number])) {
|
|
73
|
-
return `var(--${color}-600)`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return color;
|
|
81
|
+
return resolveChartColor(color);
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
function onSegmentEnter(index: number): void {
|
|
@@ -18,17 +18,27 @@
|
|
|
18
18
|
--color: var(--primary-600);
|
|
19
19
|
|
|
20
20
|
position: relative;
|
|
21
|
+
appearance: none;
|
|
21
22
|
display: flex;
|
|
22
23
|
padding: 6px;
|
|
23
24
|
margin: 0 -9px;
|
|
24
25
|
align-items: flex-start;
|
|
25
26
|
flex-flow: row nowrap;
|
|
26
27
|
gap: 9px;
|
|
28
|
+
color: inherit;
|
|
29
|
+
font: inherit;
|
|
27
30
|
font-size: 14px;
|
|
28
31
|
line-height: 20px;
|
|
32
|
+
text-align: inherit;
|
|
29
33
|
white-space: nowrap;
|
|
34
|
+
background: none;
|
|
35
|
+
border: 0;
|
|
30
36
|
border-radius: var(--radius-half);
|
|
37
|
+
cursor: default;
|
|
31
38
|
transition: background 120ms var(--swift-out);
|
|
39
|
+
z-index: 0;
|
|
40
|
+
|
|
41
|
+
@include mixin.focus-ring();
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
.statisticsLegendItem.isHovered {
|
|
@@ -61,13 +71,14 @@
|
|
|
61
71
|
text-align: right;
|
|
62
72
|
}
|
|
63
73
|
|
|
64
|
-
.statisticsLegendItem + .statisticsLegendItem::before {
|
|
74
|
+
.statisticsLegendItem:not(:focus-visible) + .statisticsLegendItem:not(:focus-visible)::before {
|
|
65
75
|
position: absolute;
|
|
66
76
|
top: -2px;
|
|
67
77
|
left: 0;
|
|
68
78
|
right: 0;
|
|
69
79
|
content: '';
|
|
70
80
|
border-top: 1px dashed var(--surface-stroke);
|
|
81
|
+
z-index: -1;
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
.statisticsLegendCompact {
|
|
@@ -88,13 +99,23 @@
|
|
|
88
99
|
.statisticsLegendItemCompact {
|
|
89
100
|
--color: var(--primary-600);
|
|
90
101
|
|
|
102
|
+
appearance: none;
|
|
91
103
|
display: flex;
|
|
104
|
+
padding: 0;
|
|
92
105
|
align-items: center;
|
|
93
106
|
flex-flow: row nowrap;
|
|
94
107
|
gap: 6px;
|
|
108
|
+
color: inherit;
|
|
109
|
+
font: inherit;
|
|
95
110
|
font-size: 14px;
|
|
96
111
|
line-height: 1;
|
|
112
|
+
text-align: inherit;
|
|
97
113
|
white-space: nowrap;
|
|
114
|
+
background: none;
|
|
115
|
+
border: 0;
|
|
116
|
+
cursor: default;
|
|
117
|
+
|
|
118
|
+
@include mixin.focus-ring();
|
|
98
119
|
|
|
99
120
|
.statisticsLegendItemColor {
|
|
100
121
|
margin: 0;
|