@flux-ui/statistics 3.0.0-next.28

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.
Files changed (57) hide show
  1. package/README.md +16 -0
  2. package/dist/component/FluxStatisticsAreaChart.vue.d.ts +9 -0
  3. package/dist/component/FluxStatisticsBarChart.vue.d.ts +9 -0
  4. package/dist/component/FluxStatisticsBase.vue.d.ts +28 -0
  5. package/dist/component/FluxStatisticsChange.vue.d.ts +9 -0
  6. package/dist/component/FluxStatisticsChart.vue.d.ts +22 -0
  7. package/dist/component/FluxStatisticsChartPane.vue.d.ts +32 -0
  8. package/dist/component/FluxStatisticsDetailsTable.vue.d.ts +23 -0
  9. package/dist/component/FluxStatisticsDetailsTableRow.vue.d.ts +7 -0
  10. package/dist/component/FluxStatisticsDonutChart.vue.d.ts +9 -0
  11. package/dist/component/FluxStatisticsGrid.vue.d.ts +26 -0
  12. package/dist/component/FluxStatisticsKpi.vue.d.ts +11 -0
  13. package/dist/component/FluxStatisticsLegend.vue.d.ts +18 -0
  14. package/dist/component/FluxStatisticsLegendItem.vue.d.ts +10 -0
  15. package/dist/component/FluxStatisticsLineChart.vue.d.ts +9 -0
  16. package/dist/component/FluxStatisticsMeter.vue.d.ts +14 -0
  17. package/dist/component/FluxStatisticsMetric.vue.d.ts +29 -0
  18. package/dist/component/FluxStatisticsPieChart.vue.d.ts +9 -0
  19. package/dist/component/index.d.ts +17 -0
  20. package/dist/index.css +492 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.js +10184 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/types.d.ts +13 -0
  25. package/package.json +77 -0
  26. package/src/component/FluxStatisticsAreaChart.vue +59 -0
  27. package/src/component/FluxStatisticsBarChart.vue +51 -0
  28. package/src/component/FluxStatisticsBase.vue +43 -0
  29. package/src/component/FluxStatisticsChange.vue +37 -0
  30. package/src/component/FluxStatisticsChart.vue +193 -0
  31. package/src/component/FluxStatisticsChartPane.vue +47 -0
  32. package/src/component/FluxStatisticsDetailsTable.vue +25 -0
  33. package/src/component/FluxStatisticsDetailsTableRow.vue +22 -0
  34. package/src/component/FluxStatisticsDonutChart.vue +32 -0
  35. package/src/component/FluxStatisticsGrid.vue +31 -0
  36. package/src/component/FluxStatisticsKpi.vue +43 -0
  37. package/src/component/FluxStatisticsLegend.vue +11 -0
  38. package/src/component/FluxStatisticsLegendItem.vue +59 -0
  39. package/src/component/FluxStatisticsLineChart.vue +59 -0
  40. package/src/component/FluxStatisticsMeter.vue +83 -0
  41. package/src/component/FluxStatisticsMetric.vue +59 -0
  42. package/src/component/FluxStatisticsPieChart.vue +32 -0
  43. package/src/component/index.ts +17 -0
  44. package/src/css/Base.module.scss +46 -0
  45. package/src/css/Change.module.scss +42 -0
  46. package/src/css/Chart.module.scss +93 -0
  47. package/src/css/ChartPane.module.scss +38 -0
  48. package/src/css/Core.module.scss +3 -0
  49. package/src/css/DetailsTable.module.scss +43 -0
  50. package/src/css/Grid.module.scss +26 -0
  51. package/src/css/Kpi.module.scss +21 -0
  52. package/src/css/Legend.module.scss +53 -0
  53. package/src/css/Meter.module.scss +98 -0
  54. package/src/css/Metric.module.scss +66 -0
  55. package/src/index.ts +48 -0
  56. package/src/types.d.ts +13 -0
  57. package/tsconfig.json +7 -0
@@ -0,0 +1,13 @@
1
+ declare module '*.module.css' {
2
+ const content: Record<string, string>;
3
+ export default content;
4
+ }
5
+
6
+ declare module '*.module.scss' {
7
+ const content: Record<string, string>;
8
+ export default content;
9
+ }
10
+
11
+ declare module '*.svg' {
12
+ export = string;
13
+ }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@flux-ui/statistics",
3
+ "description": "Statistics components for the Flux UI library.",
4
+ "version": "3.0.0-next.28",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "funding": "https://github.com/sponsors/basmilius",
8
+ "homepage": "https://flux.bas.dev",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/basmilius/flux.git",
12
+ "directory": "packages/statistics"
13
+ },
14
+ "keywords": [
15
+ "ui library",
16
+ "component library",
17
+ "design system",
18
+ "vue",
19
+ "vue 3",
20
+ "ui",
21
+ "components",
22
+ "flux",
23
+ "statistics"
24
+ ],
25
+ "scripts": {
26
+ "build": "vue-tsc && vite build",
27
+ "dev": "vite build --watch"
28
+ },
29
+ "engines": {
30
+ "node": ">=23"
31
+ },
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ },
37
+ "./style.css": "./dist/index.css"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "provenance": true
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "src",
46
+ "tsconfig.json"
47
+ ],
48
+ "main": "./dist/index.js",
49
+ "module": "./dist/index.js",
50
+ "types": "./dist/index.d.ts",
51
+ "typings": "./dist/index.d.ts",
52
+ "sideEffects": false,
53
+ "dependencies": {
54
+ "@basmilius/common": "^3.8.0",
55
+ "@basmilius/utils": "^3.8.0",
56
+ "@flux-ui/components": "3.0.0-next.28",
57
+ "@flux-ui/internals": "3.0.0-next.28",
58
+ "apexcharts": "^5.6.0",
59
+ "clsx": "^2.1.1",
60
+ "lodash-es": "^4.17.23",
61
+ "vue": "^3.6.0-beta.6",
62
+ "vue-i18n": "^11.2.8",
63
+ "vue3-apexcharts": "^1.10.0"
64
+ },
65
+ "devDependencies": {
66
+ "@basmilius/vite-preset": "^3.8.0",
67
+ "@flux-ui/types": "3.0.0-next.28",
68
+ "@types/lodash-es": "^4.17.12",
69
+ "@types/node": "^25.3.1",
70
+ "@vitejs/plugin-vue": "^6.0.4",
71
+ "@vue/tsconfig": "^0.8.1",
72
+ "sass-embedded": "^1.97.3",
73
+ "typescript": "^5.9.3",
74
+ "vite": "^8.0.0-beta.15",
75
+ "vue-tsc": "^3.2.5"
76
+ }
77
+ }
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <Chart
3
+ :aspectRatio="aspectRatio"
4
+ :options="merge({
5
+ chart: {
6
+ type: 'area',
7
+ sparkline: {
8
+ enabled: true
9
+ }
10
+ },
11
+ fill: {
12
+ gradient: {
13
+ enabled: true,
14
+ opacityFrom: 0.5,
15
+ opacityTo: 0
16
+ }
17
+ },
18
+ grid: {
19
+ show: true,
20
+ clipMarkers: false
21
+ },
22
+ legend: {
23
+ show: false
24
+ },
25
+ stroke: {
26
+ curve: 'smooth',
27
+ width: 2
28
+ }
29
+ }, options)"
30
+ :series="translatedSeries"/>
31
+ </template>
32
+
33
+ <script
34
+ lang="ts"
35
+ setup>
36
+ import type { ApexOptions } from 'apexcharts';
37
+ import { merge } from 'lodash-es';
38
+ import { computed } from 'vue';
39
+ import { useI18n } from 'vue-i18n';
40
+ import Chart from './FluxStatisticsChart.vue';
41
+
42
+ const {
43
+ options = {},
44
+ series
45
+ } = defineProps<{
46
+ readonly aspectRatio?: number;
47
+ readonly options?: ApexOptions;
48
+ readonly series: ApexOptions['series'];
49
+ }>();
50
+
51
+ const {t} = useI18n({useScope: 'parent'});
52
+
53
+ const translatedSeries = computed(() => {
54
+ return (series as any).map((seriesItem: any) => ({
55
+ ...seriesItem,
56
+ name: t(seriesItem.name as string)
57
+ }));
58
+ });
59
+ </script>
@@ -0,0 +1,51 @@
1
+ <template>
2
+ <Chart
3
+ :aspectRatio="aspectRatio"
4
+ :options="merge({
5
+ chart: {
6
+ type: 'bar'
7
+ },
8
+ grid: {
9
+ show: true,
10
+ clipMarkers: false,
11
+ padding: {
12
+ top: 21,
13
+ left: 9,
14
+ right: 9
15
+ }
16
+ },
17
+ tooltip: {
18
+ shared: true,
19
+ intersect: false
20
+ }
21
+ }, options)"
22
+ :series="translatedSeries"/>
23
+ </template>
24
+
25
+ <script
26
+ lang="ts"
27
+ setup>
28
+ import type { ApexOptions } from 'apexcharts';
29
+ import { merge } from 'lodash-es';
30
+ import { computed } from 'vue';
31
+ import { useI18n } from 'vue-i18n';
32
+ import Chart from './FluxStatisticsChart.vue';
33
+
34
+ const {
35
+ options = {},
36
+ series
37
+ } = defineProps<{
38
+ readonly aspectRatio?: number;
39
+ readonly options?: ApexOptions;
40
+ readonly series: ApexOptions['series'];
41
+ }>();
42
+
43
+ const {t} = useI18n({useScope: 'parent'});
44
+
45
+ const translatedSeries = computed(() => {
46
+ return (series as any).map((seriesItem: any) => ({
47
+ ...seriesItem,
48
+ name: t(seriesItem.name as string)
49
+ }));
50
+ });
51
+ </script>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <FluxPane :class="isSmall ? $style.statisticsBaseSmall : $style.statisticsBase">
3
+ <div
4
+ v-if="title"
5
+ :class="$style.statisticsBaseHeader">
6
+ <span :class="$style.statisticsBaseHeaderTitle">
7
+ {{ title }}
8
+ </span>
9
+
10
+ <FluxIcon
11
+ v-if="icon"
12
+ :class="$style.statisticsBaseHeaderIcon"
13
+ :name="icon"/>
14
+ </div>
15
+
16
+ <div
17
+ v-if="slots.default"
18
+ :class="$style.statisticsBaseContent">
19
+ <slot/>
20
+ </div>
21
+
22
+ <slot name="content"/>
23
+ </FluxPane>
24
+ </template>
25
+
26
+ <script
27
+ lang="ts"
28
+ setup>
29
+ import { FluxIcon, FluxPane } from '@flux-ui/components';
30
+ import type { FluxIconName } from '@flux-ui/types';
31
+ import $style from '$fluxStatistics/css/Base.module.scss';
32
+
33
+ defineProps<{
34
+ readonly icon?: FluxIconName;
35
+ readonly isSmall?: boolean;
36
+ readonly title?: string;
37
+ }>();
38
+
39
+ const slots = defineSlots<{
40
+ content?(): any;
41
+ default?(): any;
42
+ }>();
43
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <div
3
+ :class="clsx(
4
+ $style.statisticsChange,
5
+ color === 'gray' && $style.isGray,
6
+ color === 'primary' && $style.isPrimary,
7
+ color === 'danger' && $style.isDanger,
8
+ color === 'info' && $style.isInfo,
9
+ color === 'success' && $style.isSuccess,
10
+ color === 'warning' && $style.isWarning
11
+ )"
12
+ role="presentation">
13
+ <FluxIcon
14
+ v-if="icon"
15
+ :class="$style.statisticsChangeIcon"
16
+ :name="icon"/>
17
+
18
+ <span :class="$style.statisticsChangeValue">
19
+ {{ value }}
20
+ </span>
21
+ </div>
22
+ </template>
23
+
24
+ <script
25
+ lang="ts"
26
+ setup>
27
+ import { FluxIcon } from '@flux-ui/components';
28
+ import type { FluxColor, FluxIconName } from '@flux-ui/types';
29
+ import { clsx } from 'clsx';
30
+ import $style from '$fluxStatistics/css/Change.module.scss';
31
+
32
+ defineProps<{
33
+ readonly color?: FluxColor;
34
+ readonly icon?: FluxIconName;
35
+ readonly value: string | number;
36
+ }>();
37
+ </script>
@@ -0,0 +1,193 @@
1
+ <template>
2
+ <div :class="$style.statisticsChart">
3
+ <ApexCharts
4
+ ref="chart"
5
+ :class="$style.statisticsChartElement"
6
+ width="100%"
7
+ height="100%"
8
+ :options="merge({
9
+ chart: {
10
+ height: '100%',
11
+ width: '100%',
12
+ offsetX: 0,
13
+ offsetY: 0,
14
+ parentHeightOffset: 0,
15
+ redrawOnParentResize: false,
16
+ redrawOnWindowResize: false,
17
+ animations: {
18
+ enabled: true,
19
+ speed: 300,
20
+ animateGradually: {
21
+ enabled: false,
22
+ delay: 150
23
+ },
24
+ dynamicAnimation: {
25
+ enabled: true,
26
+ speed: 150
27
+ }
28
+ },
29
+ toolbar: {
30
+ show: false
31
+ },
32
+ zoom: {
33
+ enabled: false
34
+ },
35
+ events: {
36
+ mounted: (context: any) => {
37
+ const svg = context.el.querySelector('svg');
38
+ svg?.querySelectorAll('title').forEach((t: HTMLTitleElement) => t.remove());
39
+ },
40
+ updated: (context: any) => {
41
+ const svg = context.el.querySelector('svg');
42
+ svg?.querySelectorAll('title').forEach((t: HTMLTitleElement) => t.remove());
43
+ }
44
+ }
45
+ },
46
+ colors: [
47
+ 'var(--chart-1)',
48
+ 'var(--chart-2)',
49
+ 'var(--chart-3)',
50
+ 'var(--chart-4)'
51
+ ],
52
+ dataLabels: {
53
+ enabled: false
54
+ },
55
+ grid: {
56
+ borderColor: 'var(--stroke)',
57
+ padding: {
58
+ top: 0,
59
+ left: -3,
60
+ right: -3,
61
+ bottom: 0
62
+ }
63
+ },
64
+ legend: {
65
+ show: false
66
+ },
67
+ plotOptions: {
68
+ bar: {
69
+ borderRadius: 6,
70
+ columnWidth: '75%'
71
+ },
72
+ donut: {
73
+ expandOnClick: false
74
+ },
75
+ pie: {
76
+ expandOnClick: false
77
+ }
78
+ },
79
+ states: {
80
+ hover: {
81
+ filter: {
82
+ type: 'none'
83
+ }
84
+ }
85
+ },
86
+ title: {
87
+ text: undefined
88
+ },
89
+ tooltip: {
90
+ custom: generateTooltip,
91
+ followCursor: false,
92
+ hideEmptySeries: true,
93
+ onDatasetHover: {
94
+ highlightDataSeries: true
95
+ }
96
+ },
97
+ xaxis: {
98
+ labels: { show: false },
99
+ axisBorder: { show: false },
100
+ axisTicks: { show: false },
101
+ crosshairs: { show: true },
102
+ tooltip: { enabled: false }
103
+ },
104
+ yaxis: {
105
+ show: false,
106
+ labels: { show: false }
107
+ }
108
+ }, options, {series})"
109
+ :series="series"/>
110
+ </div>
111
+ </template>
112
+
113
+ <script
114
+ lang="ts"
115
+ setup>
116
+ import { useResizeObserver } from '@basmilius/common';
117
+ import { formatNumber } from '@basmilius/utils';
118
+ import type { ApexOptions } from 'apexcharts';
119
+ import { merge } from 'lodash-es';
120
+ import { toRaw, unref, useTemplateRef } from 'vue';
121
+ import { useI18n } from 'vue-i18n';
122
+ import ApexCharts from 'vue3-apexcharts';
123
+ import $style from '$fluxStatistics/css/Chart.module.scss';
124
+
125
+ const {
126
+ options = {}
127
+ } = defineProps<{
128
+ readonly options?: ApexOptions;
129
+ readonly series: ApexOptions['series'];
130
+ }>();
131
+
132
+ const chart = useTemplateRef('chart');
133
+ const {t} = useI18n({useScope: 'parent'});
134
+
135
+ useResizeObserver(chart, () => {
136
+ const _chart = unref(chart);
137
+
138
+ if (!_chart) {
139
+ return;
140
+ }
141
+
142
+ // @ts-ignore Update exists.
143
+ _chart.chart?.update();
144
+ });
145
+
146
+ function generateTooltip({dataPointIndex, w}: { dataPointIndex: number | null; w: any; }): string {
147
+ w = toRaw(w);
148
+
149
+ let labels: string[];
150
+
151
+ if (w.globals.categoryLabels && w.globals.categoryLabels.length > 0) {
152
+ labels = w.globals.categoryLabels;
153
+ } else if (w.globals.labels && w.globals.labels.length > 0) {
154
+ labels = w.globals.labels;
155
+ }
156
+
157
+ return `${labels && dataPointIndex !== null && labels[dataPointIndex] ? `
158
+ <div class="${$style.statisticsChartTooltipTitle}">
159
+ ${t(labels[dataPointIndex])}
160
+ </div>
161
+ ` : ''}
162
+
163
+ <div class="${$style.statisticsChartTooltipBody}">
164
+ ${w.globals.series.map((_: any, index: number) => `
165
+ <div class="${$style.statisticsChartTooltipSeriesColor}" style="background: ${w.globals.seriesColors[index] ?? w.globals.colors[index]}"></div>
166
+
167
+ <div class="${$style.statisticsChartTooltipSeriesName}">
168
+ ${w.globals.seriesNames[index]}
169
+ </div>
170
+
171
+ <div class="${$style.statisticsChartTooltipSeriesValue}">
172
+ ${formatSeriesValue(w, dataPointIndex, index)}
173
+ </div>
174
+ `).join('')}
175
+ </div>`;
176
+ }
177
+
178
+ function formatSeriesValue(w: any, dataPointIndex: number | null, seriesIndex: number): string {
179
+ const value = dataPointIndex !== null
180
+ ? w.globals.series[seriesIndex][dataPointIndex]
181
+ : w.globals.series[seriesIndex];
182
+
183
+ if (w.config.tooltip.y.formatter) {
184
+ return w.config.tooltip.y.formatter(value, {dataPointIndex, seriesIndex});
185
+ }
186
+
187
+ if (Number.isInteger(value)) {
188
+ return formatNumber(value);
189
+ }
190
+
191
+ return value;
192
+ }
193
+ </script>
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <Base
3
+ :class="$style.statisticsChartPane"
4
+ :icon="icon"
5
+ :title="title"
6
+ :style="{
7
+ '--aspect-ratio': aspectRatio,
8
+ '--max-height': maxHeight && `${maxHeight}px`,
9
+ '--min-height': minHeight && `${minHeight}px`
10
+ }"
11
+ #content>
12
+ <div :class="$style.statisticsChartPaneBody">
13
+ <div :class="$style.statisticsChartPaneContainer">
14
+ <slot/>
15
+ </div>
16
+
17
+ <slot name="legend"/>
18
+ </div>
19
+
20
+ <FluxToolbar v-if="slots.toolbar">
21
+ <slot name="toolbar"/>
22
+ </FluxToolbar>
23
+ </Base>
24
+ </template>
25
+
26
+ <script
27
+ lang="ts"
28
+ setup>
29
+ import { FluxToolbar } from '@flux-ui/components';
30
+ import type { FluxIconName } from '@flux-ui/types';
31
+ import Base from './FluxStatisticsBase.vue';
32
+ import $style from '$fluxStatistics/css/ChartPane.module.scss';
33
+
34
+ defineProps<{
35
+ readonly aspectRatio?: number;
36
+ readonly maxHeight?: number;
37
+ readonly minHeight?: number;
38
+ readonly icon?: FluxIconName;
39
+ readonly title?: string;
40
+ }>();
41
+
42
+ const slots = defineSlots<{
43
+ default(): any;
44
+ legend?(): any;
45
+ toolbar?(): any;
46
+ }>();
47
+ </script>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div :class="$style.detailsTable">
3
+ <div :class="$style.detailsTableTitle">
4
+ {{ title }}
5
+ </div>
6
+
7
+ <div :class="$style.detailsTableRows">
8
+ <slot/>
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script
14
+ lang="ts"
15
+ setup>
16
+ import $style from '$fluxStatistics/css/DetailsTable.module.scss';
17
+
18
+ defineProps<{
19
+ readonly title: string;
20
+ }>();
21
+
22
+ defineSlots<{
23
+ default(): any;
24
+ }>();
25
+ </script>
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <div :class="$style.detailsTableRow">
3
+ <span :class="$style.detailsTableRowLabel">
4
+ {{ label }}
5
+ </span>
6
+
7
+ <span :class="$style.detailsTableRowValue">
8
+ {{ value }}
9
+ </span>
10
+ </div>
11
+ </template>
12
+
13
+ <script
14
+ lang="ts"
15
+ setup>
16
+ import $style from '$fluxStatistics/css/DetailsTable.module.scss';
17
+
18
+ defineProps<{
19
+ readonly label: string;
20
+ readonly value: string;
21
+ }>();
22
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <Chart
3
+ :aspectRatio="aspectRatio"
4
+ :options="merge({
5
+ chart: {
6
+ type: 'donut'
7
+ },
8
+ legend: {
9
+ show: false
10
+ },
11
+ tooltip: {
12
+ enabled: false
13
+ }
14
+ }, options)"
15
+ :series="series"/>
16
+ </template>
17
+
18
+ <script
19
+ lang="ts"
20
+ setup>
21
+ import type { ApexOptions } from 'apexcharts';
22
+ import { merge } from 'lodash-es';
23
+ import Chart from './FluxStatisticsChart.vue';
24
+
25
+ const {
26
+ options = {}
27
+ } = defineProps<{
28
+ readonly aspectRatio?: number;
29
+ readonly options?: ApexOptions;
30
+ readonly series: ApexOptions['series'];
31
+ }>();
32
+ </script>
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <div
3
+ :class="$style.statisticsGrid"
4
+ :style="{
5
+ '--gap': gap && `${gap}px`,
6
+ '--xs': xs ?? 1,
7
+ '--sm': sm ?? xs ?? 1,
8
+ '--md': md ?? sm ?? xs ?? 2,
9
+ '--lg': lg ?? md ?? sm ?? xs ?? 3,
10
+ '--xl': xl ?? lg ?? md ?? sm ?? xs ?? 4
11
+ }">
12
+ <div :class="$style.statisticsGridContent">
13
+ <slot/>
14
+ </div>
15
+ </div>
16
+ </template>
17
+
18
+ <script
19
+ lang="ts"
20
+ setup>
21
+ import $style from '$fluxStatistics/css/Grid.module.scss';
22
+
23
+ defineProps<{
24
+ readonly gap?: number;
25
+ readonly xs?: number;
26
+ readonly sm?: number;
27
+ readonly md?: number;
28
+ readonly lg?: number;
29
+ readonly xl?: number;
30
+ }>();
31
+ </script>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <Base
3
+ is-small
4
+ :icon="icon"
5
+ :title="title">
6
+ <div :class="$style.statisticsKpiValue">
7
+ {{ value }}
8
+ </div>
9
+
10
+ <div
11
+ v-if="change || footer"
12
+ :class="$style.statisticsKpiBottom">
13
+ <Change
14
+ v-if="change"
15
+ :color="change.color"
16
+ :icon="change.icon"
17
+ :value="change.value"/>
18
+
19
+ <span
20
+ v-if="footer"
21
+ :class="$style.statisticsKpiFooter">
22
+ {{ footer }}
23
+ </span>
24
+ </div>
25
+ </Base>
26
+ </template>
27
+
28
+ <script
29
+ lang="ts"
30
+ setup>
31
+ import type { FluxIconName, FluxStatisticsChange } from '@flux-ui/types';
32
+ import Base from './FluxStatisticsBase.vue';
33
+ import Change from './FluxStatisticsChange.vue';
34
+ import $style from '$fluxStatistics/css/Kpi.module.scss';
35
+
36
+ defineProps<{
37
+ readonly footer?: string;
38
+ readonly change?: FluxStatisticsChange;
39
+ readonly icon?: FluxIconName;
40
+ readonly title: string;
41
+ readonly value: string | number;
42
+ }>();
43
+ </script>