@imaginario27/air-ui-ds 1.12.2 → 1.13.0
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/CHANGELOG.md +11 -0
- package/components/cards/specific/MetricCard.vue +211 -22
- package/models/enums/metrics.ts +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this package are documented in this file.
|
|
|
5
5
|
Historical releases were reconstructed from git history (GitHub repository) and npm publish dates.
|
|
6
6
|
Future releases will include detailed entries generated with Changesets.
|
|
7
7
|
|
|
8
|
+
## 1.12.2 - 2026-04-22
|
|
9
|
+
|
|
10
|
+
Release type: patch.
|
|
11
|
+
Commits found in range: 1.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
1. fix focus ring in form fields and icon span rendering when icon is not set ([4b0d548](https://github.com/imaginario27/air-ui/commit/4b0d5484271d25db6902eb1888a532291830fa4d))
|
|
16
|
+
|
|
17
|
+
- Package: @imaginario27/air-ui-ds.
|
|
18
|
+
|
|
8
19
|
## 1.12.1 - 2026-04-16
|
|
9
20
|
|
|
10
21
|
Release type: patch.
|
|
@@ -1,42 +1,231 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Card>
|
|
2
|
+
<Card :class="['gap-5!', containerClass]">
|
|
3
3
|
<CardHeader>
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
<template v-if="icon">
|
|
5
|
+
<Icon
|
|
6
|
+
v-if="
|
|
7
|
+
styleType === DashboardMetricCardStyle.NEUTRAL_FILLED ||
|
|
8
|
+
styleType === DashboardMetricCardStyle.PRIMARY_BRAND_FILLED ||
|
|
9
|
+
styleType === DashboardMetricCardStyle.SECONDARY_BRAND_FILLED ||
|
|
10
|
+
styleType === DashboardMetricCardStyle.CUSTOM_FILLED
|
|
11
|
+
"
|
|
12
|
+
:name="icon"
|
|
13
|
+
:class="textColorClass"
|
|
14
|
+
/>
|
|
15
|
+
<ContainedIcon
|
|
16
|
+
v-else
|
|
17
|
+
:icon
|
|
18
|
+
:iconSize="IconSize.SM"
|
|
19
|
+
:color="iconColor"
|
|
20
|
+
:styleType="containedIconStyleType"
|
|
21
|
+
:shape="containedIconShape"
|
|
22
|
+
class="w-6! h-6! min-w-6! min-h-6!"
|
|
23
|
+
/>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<CardTitle
|
|
27
|
+
:title
|
|
28
|
+
:class="textColorClass"
|
|
7
29
|
/>
|
|
8
30
|
</CardHeader>
|
|
9
|
-
<CardBody>
|
|
10
|
-
<div :class="['flex
|
|
11
|
-
<span class="
|
|
12
|
-
{{
|
|
31
|
+
<CardBody class="flex flex-col! gap-2!">
|
|
32
|
+
<div :class="['flex', 'items-end', 'gap-1', textColorClass]">
|
|
33
|
+
<span class="text-3xl font-semibold">
|
|
34
|
+
{{ amount }}
|
|
13
35
|
</span>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
36
|
+
|
|
37
|
+
<span
|
|
38
|
+
v-if="unit"
|
|
39
|
+
class="text-lg font-semibold"
|
|
17
40
|
>
|
|
41
|
+
/{{ unit }}
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="flex flex-col gap-0.5">
|
|
46
|
+
<p
|
|
47
|
+
v-if="featuredDescription"
|
|
48
|
+
:class="['text-sm', 'font-semibold', textColorClass]"
|
|
49
|
+
>
|
|
50
|
+
{{ featuredDescription }}
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<p :class="['text-sm', styleType === DashboardMetricCardStyle.DEFAULT ? 'text-text-neutral-subtle' : textColorClass]">
|
|
18
54
|
{{ description }}
|
|
19
55
|
</p>
|
|
20
56
|
</div>
|
|
57
|
+
|
|
58
|
+
<div
|
|
59
|
+
v-if="trend"
|
|
60
|
+
:class="['flex', 'items-center', 'gap-1', trendTextColorClass]"
|
|
61
|
+
>
|
|
62
|
+
<Icon
|
|
63
|
+
:name="trendIcon"
|
|
64
|
+
:size="IconSize.SM"
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<span class="text-sm mt-0.5">
|
|
68
|
+
{{ trend }}
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
21
71
|
</CardBody>
|
|
22
72
|
</Card>
|
|
23
73
|
</template>
|
|
74
|
+
|
|
24
75
|
<script setup lang="ts">
|
|
25
76
|
// Props
|
|
26
|
-
defineProps({
|
|
77
|
+
const props = defineProps({
|
|
78
|
+
styleType: {
|
|
79
|
+
type: String as PropType<DashboardMetricCardStyle>,
|
|
80
|
+
default: DashboardMetricCardStyle.DEFAULT,
|
|
81
|
+
validator: (value: DashboardMetricCardStyle) => Object.values(DashboardMetricCardStyle).includes(value),
|
|
82
|
+
},
|
|
27
83
|
title: {
|
|
28
84
|
type: String as PropType<string>,
|
|
29
|
-
default:
|
|
85
|
+
default: "Metric title",
|
|
86
|
+
},
|
|
87
|
+
icon: String as PropType<string>,
|
|
88
|
+
iconContainerShape: {
|
|
89
|
+
type: String as PropType<IconContainerShape>,
|
|
90
|
+
default: IconContainerShape.SQUARE,
|
|
91
|
+
validator: (value: IconContainerShape) => Object.values(IconContainerShape).includes(value),
|
|
92
|
+
},
|
|
93
|
+
defaultStyleIconColor: {
|
|
94
|
+
type: String as PropType<ColorAccent>,
|
|
95
|
+
default: ColorAccent.NEUTRAL,
|
|
96
|
+
validator: (value: ColorAccent) => Object.values(ColorAccent).includes(value),
|
|
30
97
|
},
|
|
31
|
-
|
|
98
|
+
defaultStyleIconContainerType: {
|
|
99
|
+
type: String as PropType<IconContainerStyleType>,
|
|
100
|
+
default: IconContainerStyleType.FILLED,
|
|
101
|
+
validator: (value: IconContainerStyleType) => Object.values(IconContainerStyleType).includes(value),
|
|
102
|
+
},
|
|
103
|
+
amount: {
|
|
32
104
|
type: [String, Number] as PropType<string | number>,
|
|
33
|
-
default: 0
|
|
105
|
+
default: 0,
|
|
106
|
+
},
|
|
107
|
+
unit: String as PropType<string>,
|
|
108
|
+
featuredDescription: String as PropType<string>,
|
|
109
|
+
description: {
|
|
110
|
+
type: String as PropType<string>,
|
|
111
|
+
default: "Metric description",
|
|
112
|
+
},
|
|
113
|
+
trend: String as PropType<string>,
|
|
114
|
+
trendDirection: {
|
|
115
|
+
type: String as PropType<DashboardMetricTrendDirection>,
|
|
116
|
+
default: DashboardMetricTrendDirection.UP,
|
|
117
|
+
validator: (value: DashboardMetricTrendDirection) => Object.values(DashboardMetricTrendDirection).includes(value),
|
|
34
118
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
119
|
+
customFilledColorClass: {
|
|
120
|
+
type: String as PropType<string>,
|
|
121
|
+
default: "bg-background-neutral-bold",
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Computed
|
|
126
|
+
const trendIcon = computed(() => {
|
|
127
|
+
const variant: Record<DashboardMetricTrendDirection, string> = {
|
|
128
|
+
[DashboardMetricTrendDirection.UP]: "mdi:arrow-up",
|
|
129
|
+
[DashboardMetricTrendDirection.DOWN]: "mdi:arrow-down",
|
|
130
|
+
[DashboardMetricTrendDirection.NEUTRAL]: "mdi:minus",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return variant[props.trendDirection as DashboardMetricTrendDirection] || "mdi:arrow-up";
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const containerClass = computed(() => {
|
|
137
|
+
const variant: Record<DashboardMetricCardStyle, string> = {
|
|
138
|
+
[DashboardMetricCardStyle.DEFAULT]: "bg-background-container-surface",
|
|
139
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_FILLED]: "bg-background-primary-brand-default border-border-primary-brand-default dark",
|
|
140
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_SOFT]: "bg-background-primary-brand-soft",
|
|
141
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_FILLED]: "bg-background-secondary-brand-default border-border-secondary-brand dark",
|
|
142
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_SOFT]: "bg-background-secondary-brand-soft",
|
|
143
|
+
[DashboardMetricCardStyle.NEUTRAL_FILLED]: "bg-background-neutral-bold dark",
|
|
144
|
+
[DashboardMetricCardStyle.NEUTRAL_SOFT]: "bg-background-neutral-subtler",
|
|
145
|
+
[DashboardMetricCardStyle.CUSTOM_FILLED]: props.customFilledColorClass,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return variant[props.styleType as DashboardMetricCardStyle] || "bg-background-container-surface";
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const containedIconStyleType = computed(() => {
|
|
152
|
+
if (props.styleType === DashboardMetricCardStyle.DEFAULT) {
|
|
153
|
+
return props.defaultStyleIconContainerType;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return IconContainerStyleType.FILLED;
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const containedIconShape = computed(() => {
|
|
160
|
+
if (props.styleType === DashboardMetricCardStyle.DEFAULT) {
|
|
161
|
+
return props.iconContainerShape;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return IconContainerShape.SQUARE;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const iconColor = computed(() => {
|
|
168
|
+
if (props.defaultStyleIconColor && props.styleType === DashboardMetricCardStyle.DEFAULT) {
|
|
169
|
+
return props.defaultStyleIconColor;
|
|
40
170
|
}
|
|
41
|
-
|
|
42
|
-
|
|
171
|
+
|
|
172
|
+
const variant: Record<DashboardMetricCardStyle, ColorAccent> = {
|
|
173
|
+
[DashboardMetricCardStyle.DEFAULT]: ColorAccent.NEUTRAL,
|
|
174
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_FILLED]: ColorAccent.PRIMARY_BRAND,
|
|
175
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_SOFT]: ColorAccent.PRIMARY_BRAND,
|
|
176
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_FILLED]: ColorAccent.SECONDARY_BRAND,
|
|
177
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_SOFT]: ColorAccent.SECONDARY_BRAND,
|
|
178
|
+
[DashboardMetricCardStyle.NEUTRAL_FILLED]: ColorAccent.NEUTRAL,
|
|
179
|
+
[DashboardMetricCardStyle.NEUTRAL_SOFT]: ColorAccent.NEUTRAL,
|
|
180
|
+
[DashboardMetricCardStyle.CUSTOM_FILLED]: ColorAccent.NEUTRAL,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return variant[props.styleType as DashboardMetricCardStyle] || ColorAccent.NEUTRAL;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const textColorClass = computed(() => {
|
|
187
|
+
const variant: Record<DashboardMetricCardStyle, string> = {
|
|
188
|
+
[DashboardMetricCardStyle.DEFAULT]: "text-text-neutral-default",
|
|
189
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_FILLED]: "text-text-neutral-on-filled",
|
|
190
|
+
[DashboardMetricCardStyle.PRIMARY_BRAND_SOFT]: "text-text-primary-brand-on-soft-bg",
|
|
191
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_FILLED]: "text-text-neutral-on-filled",
|
|
192
|
+
[DashboardMetricCardStyle.SECONDARY_BRAND_SOFT]: "text-text-secondary-brand-on-soft-bg",
|
|
193
|
+
[DashboardMetricCardStyle.NEUTRAL_FILLED]: "text-text-neutral-on-filled",
|
|
194
|
+
[DashboardMetricCardStyle.NEUTRAL_SOFT]: "text-text-neutral-on-neutral-bg",
|
|
195
|
+
[DashboardMetricCardStyle.CUSTOM_FILLED]: "text-text-neutral-on-filled",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return variant[props.styleType as DashboardMetricCardStyle] || "text-text-neutral-default";
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const isFilledStyle = computed(() => {
|
|
202
|
+
return [
|
|
203
|
+
DashboardMetricCardStyle.PRIMARY_BRAND_FILLED,
|
|
204
|
+
DashboardMetricCardStyle.SECONDARY_BRAND_FILLED,
|
|
205
|
+
DashboardMetricCardStyle.NEUTRAL_FILLED,
|
|
206
|
+
DashboardMetricCardStyle.CUSTOM_FILLED,
|
|
207
|
+
].includes(props.styleType as DashboardMetricCardStyle);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const trendUpClass = computed(() => {
|
|
211
|
+
return isFilledStyle.value ? "text-text-neutral-on-filled" : "text-text-success";
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const trendDownClass = computed(() => {
|
|
215
|
+
return isFilledStyle.value ? "text-text-neutral-on-filled" : "text-text-error";
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const trendNeutralClass = computed(() => {
|
|
219
|
+
return isFilledStyle.value ? "text-text-neutral-on-filled" : "text-text-neutral-subtle";
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const trendTextColorClass = computed(() => {
|
|
223
|
+
const variant: Record<DashboardMetricTrendDirection, string> = {
|
|
224
|
+
[DashboardMetricTrendDirection.UP]: trendUpClass.value,
|
|
225
|
+
[DashboardMetricTrendDirection.DOWN]: trendDownClass.value,
|
|
226
|
+
[DashboardMetricTrendDirection.NEUTRAL]: trendNeutralClass.value,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return variant[props.trendDirection as DashboardMetricTrendDirection] || trendUpClass.value;
|
|
230
|
+
});
|
|
231
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export enum DashboardMetricCardStyle {
|
|
2
|
+
DEFAULT = "default",
|
|
3
|
+
PRIMARY_BRAND_FILLED = "primary-brand-filled",
|
|
4
|
+
PRIMARY_BRAND_SOFT = "primary-brand-soft",
|
|
5
|
+
SECONDARY_BRAND_FILLED = "secondary-brand-filled",
|
|
6
|
+
SECONDARY_BRAND_SOFT = "secondary-brand-soft",
|
|
7
|
+
NEUTRAL_FILLED = "neutral-filled",
|
|
8
|
+
NEUTRAL_SOFT = "neutral-soft",
|
|
9
|
+
CUSTOM_FILLED = "custom-filled",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum DashboardMetricTrendDirection {
|
|
13
|
+
UP = "up",
|
|
14
|
+
DOWN = "down",
|
|
15
|
+
NEUTRAL = "neutral",
|
|
16
|
+
}
|