nuxt-ui-tools 1.0.1 → 1.1.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/dist/module.d.mts +3 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +37 -0
- package/dist/runtime/dashboard/components/block/dashboard-block-state.d.vue.ts +12 -0
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue +59 -0
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue.d.ts +12 -0
- package/dist/runtime/dashboard/components/block/dashboard-data-table.d.vue.ts +9 -0
- package/dist/runtime/dashboard/components/block/dashboard-data-table.vue +47 -0
- package/dist/runtime/dashboard/components/block/dashboard-data-table.vue.d.ts +9 -0
- package/dist/runtime/dashboard/components/block/dashboard-ring.d.vue.ts +34 -0
- package/dist/runtime/dashboard/components/block/dashboard-ring.vue +91 -0
- package/dist/runtime/dashboard/components/block/dashboard-ring.vue.d.ts +34 -0
- package/dist/runtime/dashboard/components/block/dashboard-row-actions.d.vue.ts +13 -0
- package/dist/runtime/dashboard/components/block/dashboard-row-actions.vue +49 -0
- package/dist/runtime/dashboard/components/block/dashboard-row-actions.vue.d.ts +13 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.d.vue.ts +34 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue +312 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue.d.ts +34 -0
- package/dist/runtime/dashboard/components/charts/renderer.d.ts +43 -0
- package/dist/runtime/dashboard/components/charts/renderer.js +34 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-donut-chart.d.vue.ts +18 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-donut-chart.vue +45 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-donut-chart.vue.d.ts +18 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-chart.d.vue.ts +14 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-chart.vue +190 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-chart.vue.d.ts +14 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-marks.d.vue.ts +13 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-marks.vue +72 -0
- package/dist/runtime/dashboard/components/charts/unovis/dashboard-xy-marks.vue.d.ts +13 -0
- package/dist/runtime/dashboard/components/charts/unovis/xy-layers.d.ts +43 -0
- package/dist/runtime/dashboard/components/charts/unovis/xy-layers.js +60 -0
- package/dist/runtime/dashboard/components/dashboard-alerts.d.vue.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-alerts.vue +226 -0
- package/dist/runtime/dashboard/components/dashboard-alerts.vue.d.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-bar-chart.d.vue.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue +131 -0
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue.d.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-bars.d.vue.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-bars.vue +187 -0
- package/dist/runtime/dashboard/components/dashboard-bars.vue.d.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-card.d.vue.ts +47 -0
- package/dist/runtime/dashboard/components/dashboard-card.vue +316 -0
- package/dist/runtime/dashboard/components/dashboard-card.vue.d.ts +47 -0
- package/dist/runtime/dashboard/components/dashboard-combo-chart.d.vue.ts +52 -0
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue +128 -0
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue.d.ts +52 -0
- package/dist/runtime/dashboard/components/dashboard-donut-chart.d.vue.ts +48 -0
- package/dist/runtime/dashboard/components/dashboard-donut-chart.vue +216 -0
- package/dist/runtime/dashboard/components/dashboard-donut-chart.vue.d.ts +48 -0
- package/dist/runtime/dashboard/components/dashboard-feed.d.vue.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-feed.vue +238 -0
- package/dist/runtime/dashboard/components/dashboard-feed.vue.d.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-funnel.d.vue.ts +34 -0
- package/dist/runtime/dashboard/components/dashboard-funnel.vue +157 -0
- package/dist/runtime/dashboard/components/dashboard-funnel.vue.d.ts +34 -0
- package/dist/runtime/dashboard/components/dashboard-gauge.d.vue.ts +55 -0
- package/dist/runtime/dashboard/components/dashboard-gauge.vue +177 -0
- package/dist/runtime/dashboard/components/dashboard-gauge.vue.d.ts +55 -0
- package/dist/runtime/dashboard/components/dashboard-grid.d.vue.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-grid.vue +49 -0
- package/dist/runtime/dashboard/components/dashboard-grid.vue.d.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-legend.d.vue.ts +8 -0
- package/dist/runtime/dashboard/components/dashboard-legend.vue +37 -0
- package/dist/runtime/dashboard/components/dashboard-legend.vue.d.ts +8 -0
- package/dist/runtime/dashboard/components/dashboard-line-chart.d.vue.ts +46 -0
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue +129 -0
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue.d.ts +46 -0
- package/dist/runtime/dashboard/components/dashboard-list.d.vue.ts +63 -0
- package/dist/runtime/dashboard/components/dashboard-list.vue +239 -0
- package/dist/runtime/dashboard/components/dashboard-list.vue.d.ts +63 -0
- package/dist/runtime/dashboard/components/dashboard-paired-bars.d.vue.ts +43 -0
- package/dist/runtime/dashboard/components/dashboard-paired-bars.vue +194 -0
- package/dist/runtime/dashboard/components/dashboard-paired-bars.vue.d.ts +43 -0
- package/dist/runtime/dashboard/components/dashboard-refresh.d.vue.ts +29 -0
- package/dist/runtime/dashboard/components/dashboard-refresh.vue +86 -0
- package/dist/runtime/dashboard/components/dashboard-refresh.vue.d.ts +29 -0
- package/dist/runtime/dashboard/components/dashboard-relative-time.d.vue.ts +12 -0
- package/dist/runtime/dashboard/components/dashboard-relative-time.vue +23 -0
- package/dist/runtime/dashboard/components/dashboard-relative-time.vue.d.ts +12 -0
- package/dist/runtime/dashboard/components/dashboard-stack-bar.d.vue.ts +36 -0
- package/dist/runtime/dashboard/components/dashboard-stack-bar.vue +162 -0
- package/dist/runtime/dashboard/components/dashboard-stack-bar.vue.d.ts +36 -0
- package/dist/runtime/dashboard/components/dashboard-stat.d.vue.ts +69 -0
- package/dist/runtime/dashboard/components/dashboard-stat.vue +279 -0
- package/dist/runtime/dashboard/components/dashboard-stat.vue.d.ts +69 -0
- package/dist/runtime/dashboard/components/dashboard-stats.d.vue.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-stats.vue +202 -0
- package/dist/runtime/dashboard/components/dashboard-stats.vue.d.ts +38 -0
- package/dist/runtime/dashboard/components/dashboard-table.d.vue.ts +52 -0
- package/dist/runtime/dashboard/components/dashboard-table.vue +305 -0
- package/dist/runtime/dashboard/components/dashboard-table.vue.d.ts +52 -0
- package/dist/runtime/dashboard/components/dashboard-tabs.d.vue.ts +35 -0
- package/dist/runtime/dashboard/components/dashboard-tabs.vue +55 -0
- package/dist/runtime/dashboard/components/dashboard-tabs.vue.d.ts +35 -0
- package/dist/runtime/dashboard/components/dashboard-total.d.vue.ts +10 -0
- package/dist/runtime/dashboard/components/dashboard-total.vue +32 -0
- package/dist/runtime/dashboard/components/dashboard-total.vue.d.ts +10 -0
- package/dist/runtime/dashboard/components/dashboard-widget.d.vue.ts +46 -0
- package/dist/runtime/dashboard/components/dashboard-widget.vue +84 -0
- package/dist/runtime/dashboard/components/dashboard-widget.vue.d.ts +46 -0
- package/dist/runtime/dashboard/composables/use-dashboard-api.d.ts +15 -0
- package/dist/runtime/dashboard/composables/use-dashboard-api.js +91 -0
- package/dist/runtime/dashboard/composables/use-dashboard-block.d.ts +22 -0
- package/dist/runtime/dashboard/composables/use-dashboard-block.js +35 -0
- package/dist/runtime/dashboard/composables/use-dashboard-chart.d.ts +36 -0
- package/dist/runtime/dashboard/composables/use-dashboard-chart.js +118 -0
- package/dist/runtime/dashboard/composables/use-dashboard-derived.d.ts +12 -0
- package/dist/runtime/dashboard/composables/use-dashboard-derived.js +47 -0
- package/dist/runtime/dashboard/composables/use-dashboard-format.d.ts +6 -0
- package/dist/runtime/dashboard/composables/use-dashboard-format.js +12 -0
- package/dist/runtime/dashboard/composables/use-dashboard-options.d.ts +8 -0
- package/dist/runtime/dashboard/composables/use-dashboard-options.js +63 -0
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.d.ts +30 -0
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.js +71 -0
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.d.ts +12 -0
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +117 -0
- package/dist/runtime/dashboard/composables/use-dashboard-resource.d.ts +35 -0
- package/dist/runtime/dashboard/composables/use-dashboard-resource.js +80 -0
- package/dist/runtime/dashboard/composables/use-dashboard-scope.d.ts +46 -0
- package/dist/runtime/dashboard/composables/use-dashboard-scope.js +127 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.d.ts +13 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.js +40 -0
- package/dist/runtime/dashboard/composables/use-dashboard-ui.d.ts +22 -0
- package/dist/runtime/dashboard/composables/use-dashboard-ui.js +19 -0
- package/dist/runtime/dashboard/composables/use-dashboard-views.d.ts +46 -0
- package/dist/runtime/dashboard/composables/use-dashboard-views.js +49 -0
- package/dist/runtime/dashboard/composables/use-dashboard.d.ts +24 -0
- package/dist/runtime/dashboard/composables/use-dashboard.js +42 -0
- package/dist/runtime/dashboard/constants/query.d.ts +16 -0
- package/dist/runtime/dashboard/constants/query.js +15 -0
- package/dist/runtime/dashboard/index.d.ts +4 -0
- package/dist/runtime/dashboard/index.js +3 -0
- package/dist/runtime/dashboard/schema/index.d.ts +57 -0
- package/dist/runtime/dashboard/schema/index.js +3 -0
- package/dist/runtime/dashboard/types/blocks.d.ts +251 -0
- package/dist/runtime/dashboard/types/blocks.js +0 -0
- package/dist/runtime/dashboard/types/charts.d.ts +60 -0
- package/dist/runtime/dashboard/types/charts.js +0 -0
- package/dist/runtime/dashboard/types/index.d.ts +7 -0
- package/dist/runtime/dashboard/types/index.js +0 -0
- package/dist/runtime/dashboard/types/options.d.ts +46 -0
- package/dist/runtime/dashboard/types/options.js +0 -0
- package/dist/runtime/dashboard/types/params.d.ts +115 -0
- package/dist/runtime/dashboard/types/params.js +0 -0
- package/dist/runtime/dashboard/types/resource.d.ts +106 -0
- package/dist/runtime/dashboard/types/resource.js +0 -0
- package/dist/runtime/dashboard/types/runtime.d.ts +44 -0
- package/dist/runtime/dashboard/types/runtime.js +0 -0
- package/dist/runtime/dashboard/types/schema.d.ts +171 -0
- package/dist/runtime/dashboard/types/schema.js +0 -0
- package/dist/runtime/dashboard/types/ui.d.ts +201 -0
- package/dist/runtime/dashboard/types/ui.js +0 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-params.d.ts +85 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-params.js +87 -0
- package/dist/runtime/dashboard/utils/chart-frame.d.ts +5 -0
- package/dist/runtime/dashboard/utils/chart-frame.js +27 -0
- package/dist/runtime/dashboard/utils/charts.d.ts +61 -0
- package/dist/runtime/dashboard/utils/charts.js +142 -0
- package/dist/runtime/dashboard/utils/comparison.d.ts +15 -0
- package/dist/runtime/dashboard/utils/comparison.js +19 -0
- package/dist/runtime/dashboard/utils/export.d.ts +13 -0
- package/dist/runtime/dashboard/utils/export.js +45 -0
- package/dist/runtime/dashboard/utils/options.d.ts +12 -0
- package/dist/runtime/dashboard/utils/options.js +23 -0
- package/dist/runtime/dashboard/utils/params/codecs.d.ts +14 -0
- package/dist/runtime/dashboard/utils/params/codecs.js +69 -0
- package/dist/runtime/dashboard/utils/resource.d.ts +16 -0
- package/dist/runtime/dashboard/utils/resource.js +39 -0
- package/dist/runtime/dashboard/utils/schema.d.ts +9 -0
- package/dist/runtime/dashboard/utils/schema.js +31 -0
- package/dist/runtime/dashboard/utils/sparkline.d.ts +21 -0
- package/dist/runtime/dashboard/utils/sparkline.js +53 -0
- package/dist/runtime/dashboard/utils/state.d.ts +28 -0
- package/dist/runtime/dashboard/utils/state.js +32 -0
- package/dist/runtime/dashboard/utils/time.d.ts +17 -0
- package/dist/runtime/dashboard/utils/time.js +60 -0
- package/dist/runtime/dashboard/utils/tracker.d.ts +12 -0
- package/dist/runtime/dashboard/utils/tracker.js +31 -0
- package/dist/runtime/dashboard/utils/ui.d.ts +38 -0
- package/dist/runtime/dashboard/utils/ui.js +24 -0
- package/dist/runtime/dashboard/utils/visibility.d.ts +2 -0
- package/dist/runtime/dashboard/utils/visibility.js +25 -0
- package/dist/runtime/form/types/options.d.ts +15 -36
- package/dist/runtime/i18n/locales/en.js +62 -0
- package/dist/runtime/i18n/locales/fr.js +62 -0
- package/dist/runtime/i18n/types.d.ts +67 -0
- package/dist/runtime/shared/index.d.ts +2 -0
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/types/query.d.ts +10 -0
- package/dist/runtime/shared/types/query.js +0 -0
- package/dist/runtime/shared/types/remote-options.d.ts +37 -0
- package/dist/runtime/shared/types/remote-options.js +0 -0
- package/dist/runtime/spreadsheet/components/layout/spreadsheet-import-footer.d.vue.ts +2 -2
- package/dist/runtime/spreadsheet/components/layout/spreadsheet-import-footer.vue.d.ts +2 -2
- package/dist/runtime/spreadsheet/composables/use-spreadsheet-internals.d.ts +15 -15
- package/dist/runtime/spreadsheet/composables/use-spreadsheet-resolutions.d.ts +2 -2
- package/dist/runtime/spreadsheet/schema/normalize.d.ts +1 -1
- package/dist/runtime/spreadsheet/utils/references/sources.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-content.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-content.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-infinite-loader.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-infinite-loader.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-layout-switch.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-layout-switch.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-pagination.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-pagination.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-sort-menu.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-sort-menu.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/sort-menu-panel.d.vue.ts +2 -2
- package/dist/runtime/table/components/data-list/sort-menu-panel.vue.d.ts +2 -2
- package/dist/runtime/table/components/filters/shared/filter-match-mode-button.d.vue.ts +1 -1
- package/dist/runtime/table/components/filters/shared/filter-match-mode-button.vue.d.ts +1 -1
- package/dist/runtime/table/components/layout/table-footer.d.vue.ts +1 -1
- package/dist/runtime/table/components/layout/table-footer.vue.d.ts +1 -1
- package/dist/runtime/table/components/table/table-cell-ellipsis.d.ts +1 -1
- package/dist/runtime/table/composables/use-query-state.d.ts +3 -3
- package/dist/runtime/table/composables/use-table-columns.d.ts +3 -3
- package/dist/runtime/table/composables/use-table-internals.d.ts +27 -27
- package/dist/runtime/table/composables/use-table-pagination.d.ts +2 -2
- package/dist/runtime/table/composables/use-table-selection.d.ts +1 -1
- package/dist/runtime/table/composables/use-table-state.d.ts +3 -3
- package/dist/runtime/table/types/source.d.ts +4 -5
- package/dist/runtime/table/utils/columns/menu.d.ts +1 -1
- package/dist/runtime/table/utils/columns/state.d.ts +1 -1
- package/dist/runtime/table/utils/data-list-ui.d.ts +1 -1
- package/package.json +28 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
import { computed } from "vue";
|
|
5
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
6
|
+
import { isNumber, isString } from "#ui-tools/shared/utils/predicate";
|
|
7
|
+
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
8
|
+
import { useDashboardFormat } from "../composables/use-dashboard-format";
|
|
9
|
+
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
|
+
import { resolveDashboardColor } from "../utils/charts";
|
|
11
|
+
import { resolveDashboardSparkBars, resolveDashboardSparkline } from "../utils/sparkline";
|
|
12
|
+
import { resolveDashboardClasses } from "../utils/ui";
|
|
13
|
+
import DashboardSkeleton from "./block/dashboard-skeleton.vue";
|
|
14
|
+
import DashboardCard from "./dashboard-card.vue";
|
|
15
|
+
const {
|
|
16
|
+
source,
|
|
17
|
+
card = true,
|
|
18
|
+
menu = void 0,
|
|
19
|
+
freshness = void 0,
|
|
20
|
+
value,
|
|
21
|
+
format,
|
|
22
|
+
delta,
|
|
23
|
+
deltaFormat,
|
|
24
|
+
invertDelta = false,
|
|
25
|
+
caption,
|
|
26
|
+
label,
|
|
27
|
+
icon,
|
|
28
|
+
trend,
|
|
29
|
+
trendType = "area",
|
|
30
|
+
trendColor = "series-1",
|
|
31
|
+
compare,
|
|
32
|
+
compareLabel,
|
|
33
|
+
goal,
|
|
34
|
+
status,
|
|
35
|
+
ui,
|
|
36
|
+
...block
|
|
37
|
+
} = defineProps({
|
|
38
|
+
size: { type: String, required: false },
|
|
39
|
+
rows: { type: String, required: false },
|
|
40
|
+
card: { type: Boolean, required: false },
|
|
41
|
+
activation: { type: String, required: false },
|
|
42
|
+
empty: { type: Object, required: false },
|
|
43
|
+
menu: { type: [Boolean, Array, Function], required: false },
|
|
44
|
+
actions: { type: Array, required: false },
|
|
45
|
+
freshness: { type: Boolean, required: false },
|
|
46
|
+
source: { type: Object, required: true },
|
|
47
|
+
label: { type: [String, Number, Function], required: true },
|
|
48
|
+
value: { type: Function, required: true },
|
|
49
|
+
format: { type: Function, required: false },
|
|
50
|
+
delta: { type: Function, required: false },
|
|
51
|
+
deltaFormat: { type: Function, required: false },
|
|
52
|
+
invertDelta: { type: Boolean, required: false },
|
|
53
|
+
caption: { type: [String, Function], required: false },
|
|
54
|
+
icon: { type: String, required: false },
|
|
55
|
+
trend: { type: Function, required: false },
|
|
56
|
+
trendType: { type: String, required: false },
|
|
57
|
+
trendColor: { type: String, required: false },
|
|
58
|
+
compare: { type: Function, required: false },
|
|
59
|
+
compareLabel: { type: [String, Number, Function], required: false },
|
|
60
|
+
goal: { type: Function, required: false },
|
|
61
|
+
status: { type: Function, required: false },
|
|
62
|
+
ui: { type: Object, required: false }
|
|
63
|
+
});
|
|
64
|
+
defineSlots();
|
|
65
|
+
const { t } = useUiToolsLocale();
|
|
66
|
+
const formats = useDashboardFormat();
|
|
67
|
+
const appUi = useDashboardUi();
|
|
68
|
+
const ready = computed(() => {
|
|
69
|
+
const data = source.data;
|
|
70
|
+
return data === void 0 ? null : { data };
|
|
71
|
+
});
|
|
72
|
+
const resolved = computed(() => ready.value ? value(ready.value.data) : void 0);
|
|
73
|
+
const formatValue = computed(() => format ?? formats.number.value);
|
|
74
|
+
const display = computed(() => {
|
|
75
|
+
if (resolved.value === void 0) return "";
|
|
76
|
+
return isNumber(resolved.value) ? formatValue.value(resolved.value) : resolveTextValue(resolved.value);
|
|
77
|
+
});
|
|
78
|
+
const previous = computed(() => {
|
|
79
|
+
if (!ready.value || !compare) return null;
|
|
80
|
+
const amount = compare(ready.value.data);
|
|
81
|
+
return isNumber(amount) && Number.isFinite(amount) ? amount : null;
|
|
82
|
+
});
|
|
83
|
+
const change = computed(() => {
|
|
84
|
+
if (!ready.value) return null;
|
|
85
|
+
const amount = delta ? delta(ready.value.data) : previous.value !== null && previous.value !== 0 && isNumber(resolved.value) ? (resolved.value - previous.value) / Math.abs(previous.value) * 100 : null;
|
|
86
|
+
if (amount === null || amount === void 0 || !Number.isFinite(amount)) return null;
|
|
87
|
+
const good = invertDelta ? amount <= 0 : amount >= 0;
|
|
88
|
+
return { good, label: (deltaFormat ?? formats.delta.value)(amount), up: amount >= 0 };
|
|
89
|
+
});
|
|
90
|
+
const captionText = computed(() => {
|
|
91
|
+
if (!ready.value) return "";
|
|
92
|
+
if (caption !== void 0)
|
|
93
|
+
return isString(caption) ? caption : resolveTextValue(caption(ready.value.data));
|
|
94
|
+
if (previous.value === null) return "";
|
|
95
|
+
const before = formatValue.value(previous.value);
|
|
96
|
+
if (compareLabel === void 0) return t("dashboard.compare.caption", { value: before });
|
|
97
|
+
return resolveTextValue(
|
|
98
|
+
isString(compareLabel) || isNumber(compareLabel) ? compareLabel : compareLabel(before)
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
const sparkline = computed(() => {
|
|
102
|
+
if (!ready.value || !trend || trendType === "bars") return null;
|
|
103
|
+
return resolveDashboardSparkline(trend(ready.value.data) ?? []);
|
|
104
|
+
});
|
|
105
|
+
const sparkBars = computed(() => {
|
|
106
|
+
if (!ready.value || !trend || trendType !== "bars") return null;
|
|
107
|
+
return resolveDashboardSparkBars(trend(ready.value.data) ?? []);
|
|
108
|
+
});
|
|
109
|
+
const progress = computed(() => {
|
|
110
|
+
if (!ready.value || !goal || !isNumber(resolved.value)) return null;
|
|
111
|
+
const target = goal(ready.value.data);
|
|
112
|
+
if (!isNumber(target) || target <= 0) return null;
|
|
113
|
+
const completion = resolved.value / target * 100;
|
|
114
|
+
const filled = Math.round(Math.max(0, Math.min(100, completion)));
|
|
115
|
+
return {
|
|
116
|
+
completion: formats.percent.value(completion),
|
|
117
|
+
filled,
|
|
118
|
+
goal: t("dashboard.stat.goal", { value: formatValue.value(target) }),
|
|
119
|
+
reached: completion >= 100
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
const statusInfo = computed(() => {
|
|
123
|
+
const current = ready.value && status ? status(ready.value.data) : null;
|
|
124
|
+
if (!current) return null;
|
|
125
|
+
return {
|
|
126
|
+
color: resolveDashboardColor(current.color, 0),
|
|
127
|
+
label: resolveTextValue(current.label),
|
|
128
|
+
tone: current.color
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
const classes = computed(
|
|
132
|
+
() => resolveDashboardClasses(
|
|
133
|
+
{
|
|
134
|
+
caption: "min-w-0",
|
|
135
|
+
delta: "shrink-0 font-semibold tabular-nums",
|
|
136
|
+
goal: "mt-1.5 flex items-baseline justify-between gap-2 text-[11.5px] text-muted tabular-nums",
|
|
137
|
+
label: "text-[12.5px] font-normal tracking-normal text-muted",
|
|
138
|
+
mark: "me-0.5 text-[9px]",
|
|
139
|
+
meta: "mt-2.5 flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-xs leading-[1.35] text-muted",
|
|
140
|
+
progress: "mt-3.5 h-1.5 overflow-hidden rounded-full bg-[var(--nut-dash-track)]",
|
|
141
|
+
status: "inline-flex items-center gap-1.5 text-[11.5px] font-medium",
|
|
142
|
+
trend: "mt-3 h-8 w-full overflow-visible",
|
|
143
|
+
value: "mt-1.5 text-[26px] leading-none font-semibold tracking-tight text-highlighted tabular-nums"
|
|
144
|
+
},
|
|
145
|
+
appUi.value.stat,
|
|
146
|
+
ui
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
const cardUi = computed(() => ({
|
|
150
|
+
...ui,
|
|
151
|
+
header: twMerge("mb-2.5", ui?.header),
|
|
152
|
+
title: classes.value.label
|
|
153
|
+
}));
|
|
154
|
+
const trendStroke = computed(() => resolveDashboardColor(trendColor, 0));
|
|
155
|
+
const extra = computed(() => trend ? "trend" : goal ? "goal" : void 0);
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<template>
|
|
159
|
+
<DashboardCard
|
|
160
|
+
v-bind="block"
|
|
161
|
+
:card
|
|
162
|
+
:menu
|
|
163
|
+
:freshness
|
|
164
|
+
:source
|
|
165
|
+
:title="label"
|
|
166
|
+
:ui="cardUi"
|
|
167
|
+
:expandable="false"
|
|
168
|
+
>
|
|
169
|
+
<template #skeleton>
|
|
170
|
+
<DashboardSkeleton kind="stat" :extra />
|
|
171
|
+
</template>
|
|
172
|
+
<template v-if="icon || statusInfo" #header-right>
|
|
173
|
+
<span
|
|
174
|
+
v-if="statusInfo"
|
|
175
|
+
:data-status="statusInfo.tone"
|
|
176
|
+
:class="classes.status"
|
|
177
|
+
:style="{ color: statusInfo.color }"
|
|
178
|
+
>
|
|
179
|
+
<span aria-hidden="true" class="size-1.5 rounded-full bg-current" />
|
|
180
|
+
{{ statusInfo.label }}
|
|
181
|
+
</span>
|
|
182
|
+
<UIcon v-if="icon" :name="icon" class="size-4 text-dimmed" />
|
|
183
|
+
</template>
|
|
184
|
+
|
|
185
|
+
<template v-if="ready">
|
|
186
|
+
<div :class="classes.value">
|
|
187
|
+
<slot name="value" :data="ready.data" :value="display">{{ display }}</slot>
|
|
188
|
+
</div>
|
|
189
|
+
<div v-if="change || captionText || $slots.caption" :class="classes.meta">
|
|
190
|
+
<span
|
|
191
|
+
v-if="change"
|
|
192
|
+
:data-trend="change.good ? 'up' : 'down'"
|
|
193
|
+
:class="[
|
|
194
|
+
classes.delta,
|
|
195
|
+
change.good ? 'text-[var(--nut-dash-up)]' : 'text-[var(--nut-dash-down)]'
|
|
196
|
+
]"
|
|
197
|
+
>
|
|
198
|
+
<span
|
|
199
|
+
aria-hidden="true"
|
|
200
|
+
:class="[
|
|
201
|
+
classes.mark,
|
|
202
|
+
change.good ? 'text-[var(--nut-dash-up-mark)]' : 'text-[var(--nut-dash-down)]'
|
|
203
|
+
]"
|
|
204
|
+
>{{ change.up ? "\u25B2" : "\u25BC" }}</span
|
|
205
|
+
>{{ change.label }}
|
|
206
|
+
</span>
|
|
207
|
+
<span :class="classes.caption">
|
|
208
|
+
<slot name="caption" :data="ready.data">{{ captionText }}</slot>
|
|
209
|
+
</span>
|
|
210
|
+
</div>
|
|
211
|
+
<svg
|
|
212
|
+
v-if="sparkline"
|
|
213
|
+
:class="classes.trend"
|
|
214
|
+
viewBox="0 0 100 32"
|
|
215
|
+
preserveAspectRatio="none"
|
|
216
|
+
aria-hidden="true"
|
|
217
|
+
data-sparkline
|
|
218
|
+
>
|
|
219
|
+
<path
|
|
220
|
+
v-if="trendType === 'area'"
|
|
221
|
+
:d="sparkline.area"
|
|
222
|
+
:fill="trendStroke"
|
|
223
|
+
fill-opacity="0.12"
|
|
224
|
+
/>
|
|
225
|
+
<path
|
|
226
|
+
:d="sparkline.line"
|
|
227
|
+
fill="none"
|
|
228
|
+
:stroke="trendStroke"
|
|
229
|
+
stroke-width="1.75"
|
|
230
|
+
stroke-linecap="round"
|
|
231
|
+
stroke-linejoin="round"
|
|
232
|
+
vector-effect="non-scaling-stroke"
|
|
233
|
+
/>
|
|
234
|
+
</svg>
|
|
235
|
+
<svg
|
|
236
|
+
v-if="sparkBars"
|
|
237
|
+
:class="classes.trend"
|
|
238
|
+
viewBox="0 0 100 32"
|
|
239
|
+
preserveAspectRatio="none"
|
|
240
|
+
aria-hidden="true"
|
|
241
|
+
data-sparkline="bars"
|
|
242
|
+
>
|
|
243
|
+
<rect
|
|
244
|
+
v-for="(bar, index) in sparkBars"
|
|
245
|
+
:key="index"
|
|
246
|
+
:x="bar.x"
|
|
247
|
+
:y="bar.y"
|
|
248
|
+
:width="bar.width"
|
|
249
|
+
:height="bar.height"
|
|
250
|
+
rx="1"
|
|
251
|
+
:fill="trendStroke"
|
|
252
|
+
:fill-opacity="bar.last ? 1 : 0.35"
|
|
253
|
+
/>
|
|
254
|
+
</svg>
|
|
255
|
+
<template v-if="progress">
|
|
256
|
+
<div
|
|
257
|
+
role="progressbar"
|
|
258
|
+
:aria-valuenow="progress.filled"
|
|
259
|
+
aria-valuemin="0"
|
|
260
|
+
aria-valuemax="100"
|
|
261
|
+
:aria-label="progress.goal"
|
|
262
|
+
:class="classes.progress"
|
|
263
|
+
>
|
|
264
|
+
<i
|
|
265
|
+
class="block h-full rounded-full transition-[width] duration-500 ease-out"
|
|
266
|
+
:style="{
|
|
267
|
+
background: progress.reached ? 'var(--nut-dash-up-mark)' : trendStroke,
|
|
268
|
+
width: `${progress.filled}%`
|
|
269
|
+
}"
|
|
270
|
+
/>
|
|
271
|
+
</div>
|
|
272
|
+
<div :class="classes.goal">
|
|
273
|
+
<span>{{ progress.goal }}</span>
|
|
274
|
+
<b class="font-semibold text-highlighted">{{ progress.completion }}</b>
|
|
275
|
+
</div>
|
|
276
|
+
</template>
|
|
277
|
+
</template>
|
|
278
|
+
</DashboardCard>
|
|
279
|
+
</template>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { LazyTextValue } from '#ui-tools/shared/types/utils';
|
|
2
|
+
import type { DashboardBlockBaseProps, DashboardBlockUi, DashboardSeriesColor, DashboardSourceLike, DashboardStatUi, DashboardStatus, DashboardValueFormat } from '../types/index.js';
|
|
3
|
+
declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<Omit<DashboardBlockBaseProps, "ui" | "title" | "subtitle"> & {
|
|
5
|
+
source: DashboardSourceLike<TData>;
|
|
6
|
+
/** KPI label, shown above the value. */
|
|
7
|
+
label: LazyTextValue;
|
|
8
|
+
/** Main value. Numbers go through `format` (locale number format by default). */
|
|
9
|
+
value: (data: TData & ({} | null)) => LazyTextValue;
|
|
10
|
+
format?: DashboardValueFormat;
|
|
11
|
+
/**
|
|
12
|
+
* Change indicator, in percent by default. `null` hides it. Defaults to the change from
|
|
13
|
+
* `compare` when that is set.
|
|
14
|
+
*/
|
|
15
|
+
delta?: (data: TData & ({} | null)) => number | null | undefined;
|
|
16
|
+
deltaFormat?: DashboardValueFormat;
|
|
17
|
+
/** A decrease is good news (e.g. a failure rate): colors the delta accordingly. */
|
|
18
|
+
invertDelta?: boolean;
|
|
19
|
+
/** Secondary text after the delta. Wraps under it when space runs out. */
|
|
20
|
+
caption?: string | ((data: TData & ({} | null)) => LazyTextValue | undefined);
|
|
21
|
+
icon?: string;
|
|
22
|
+
/** Recent values, oldest first, drawn as a sparkline under the value. `null` leaves a gap. */
|
|
23
|
+
trend?: (data: TData & ({} | null)) => readonly (number | null | undefined)[] | undefined;
|
|
24
|
+
/** How `trend` is drawn: a filled line (default), a bare line, or mini bars. */
|
|
25
|
+
trendType?: "area" | "line" | "bars";
|
|
26
|
+
trendColor?: DashboardSeriesColor;
|
|
27
|
+
/**
|
|
28
|
+
* The same measure over the comparison period (previous period, previous year). Gives the
|
|
29
|
+
* default `delta`, and the default caption ("vs 1,204 previous period").
|
|
30
|
+
*/
|
|
31
|
+
compare?: (data: TData & ({} | null)) => number | null | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Caption shown with `compare`, as text or built from the formatted previous value. Defaults to
|
|
34
|
+
* "vs <value> previous period".
|
|
35
|
+
*/
|
|
36
|
+
compareLabel?: LazyTextValue | ((previous: string) => LazyTextValue);
|
|
37
|
+
/**
|
|
38
|
+
* Target of a numeric `value`: draws a progress bar with the goal and the completion. `null`
|
|
39
|
+
* hides it.
|
|
40
|
+
*/
|
|
41
|
+
goal?: (data: TData & ({} | null)) => number | null | undefined;
|
|
42
|
+
/** Status on the title row (a colored dot and label). `null` hides it. */
|
|
43
|
+
status?: (data: TData & ({} | null)) => DashboardStatus | null | undefined;
|
|
44
|
+
ui?: DashboardBlockUi & DashboardStatUi;
|
|
45
|
+
}> & (typeof globalThis extends {
|
|
46
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
47
|
+
} ? P : {});
|
|
48
|
+
expose: (exposed: {}) => void;
|
|
49
|
+
attrs: any;
|
|
50
|
+
slots: {
|
|
51
|
+
value?: (props: {
|
|
52
|
+
data: TData & ({} | null);
|
|
53
|
+
value: string;
|
|
54
|
+
}) => unknown;
|
|
55
|
+
caption?: (props: {
|
|
56
|
+
data: TData & ({} | null);
|
|
57
|
+
}) => unknown;
|
|
58
|
+
};
|
|
59
|
+
emit: {};
|
|
60
|
+
}>) => import("vue").VNode & {
|
|
61
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
62
|
+
};
|
|
63
|
+
declare const _default: typeof __VLS_export;
|
|
64
|
+
export default _default;
|
|
65
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
66
|
+
[K in keyof T]: T[K];
|
|
67
|
+
} : {
|
|
68
|
+
[K in keyof T as K]: T[K];
|
|
69
|
+
}) & {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DashboardBlockBaseProps, DashboardBlockUi, DashboardSourceLike, DashboardStatsItem, DashboardStatsUi } from '../types/index.js';
|
|
2
|
+
declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<Omit<DashboardBlockBaseProps, "ui"> & {
|
|
4
|
+
source: DashboardSourceLike<TData>;
|
|
5
|
+
/** Figures read from the source data, in display order. */
|
|
6
|
+
items: readonly DashboardStatsItem<TData & ({} | null)>[];
|
|
7
|
+
/**
|
|
8
|
+
* Column count, responsive: `"2 md:4"`. Defaults to one column per figure (up to 4), two on
|
|
9
|
+
* small screens.
|
|
10
|
+
*/
|
|
11
|
+
columns?: string;
|
|
12
|
+
/**
|
|
13
|
+
* `plain`: figures side by side. `divided`: 1px rules between them. `tiles`: each figure in its
|
|
14
|
+
* own bordered box.
|
|
15
|
+
*/
|
|
16
|
+
variant?: "plain" | "divided" | "tiles";
|
|
17
|
+
ui?: DashboardBlockUi & DashboardStatsUi;
|
|
18
|
+
}> & (typeof globalThis extends {
|
|
19
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
20
|
+
} ? P : {});
|
|
21
|
+
expose: (exposed: {}) => void;
|
|
22
|
+
attrs: any;
|
|
23
|
+
slots: {
|
|
24
|
+
'header-right'?: () => unknown;
|
|
25
|
+
toolbar?: () => unknown;
|
|
26
|
+
footer?: () => unknown;
|
|
27
|
+
};
|
|
28
|
+
emit: {};
|
|
29
|
+
}>) => import("vue").VNode & {
|
|
30
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
31
|
+
};
|
|
32
|
+
declare const _default: typeof __VLS_export;
|
|
33
|
+
export default _default;
|
|
34
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
35
|
+
[K in keyof T]: T[K];
|
|
36
|
+
} : {
|
|
37
|
+
[K in keyof T as K]: T[K];
|
|
38
|
+
}) & {};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
5
|
+
import { useResponsiveValue } from "#ui-tools/shared/composables/use-responsive-value";
|
|
6
|
+
import { isNumber, isString } from "#ui-tools/shared/utils/predicate";
|
|
7
|
+
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
8
|
+
import { useDashboardFormat } from "../composables/use-dashboard-format";
|
|
9
|
+
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
|
+
import { resolveDashboardColor } from "../utils/charts";
|
|
11
|
+
import { toDashboardCell } from "../utils/export";
|
|
12
|
+
import { resolveDashboardClasses } from "../utils/ui";
|
|
13
|
+
import DashboardSkeleton from "./block/dashboard-skeleton.vue";
|
|
14
|
+
import DashboardCard from "./dashboard-card.vue";
|
|
15
|
+
const {
|
|
16
|
+
source,
|
|
17
|
+
card = true,
|
|
18
|
+
menu = void 0,
|
|
19
|
+
freshness = void 0,
|
|
20
|
+
items,
|
|
21
|
+
columns,
|
|
22
|
+
variant = "plain",
|
|
23
|
+
ui,
|
|
24
|
+
...block
|
|
25
|
+
} = defineProps({
|
|
26
|
+
title: { type: [String, Number, Function], required: false },
|
|
27
|
+
subtitle: { type: [String, Number, Function], required: false },
|
|
28
|
+
size: { type: String, required: false },
|
|
29
|
+
rows: { type: String, required: false },
|
|
30
|
+
card: { type: Boolean, required: false },
|
|
31
|
+
activation: { type: String, required: false },
|
|
32
|
+
empty: { type: Object, required: false },
|
|
33
|
+
menu: { type: [Boolean, Array, Function], required: false },
|
|
34
|
+
actions: { type: Array, required: false },
|
|
35
|
+
freshness: { type: Boolean, required: false },
|
|
36
|
+
source: { type: Object, required: true },
|
|
37
|
+
items: { type: Array, required: true },
|
|
38
|
+
columns: { type: String, required: false },
|
|
39
|
+
variant: { type: String, required: false },
|
|
40
|
+
ui: { type: Object, required: false }
|
|
41
|
+
});
|
|
42
|
+
defineSlots();
|
|
43
|
+
const { t } = useUiToolsLocale();
|
|
44
|
+
const formats = useDashboardFormat();
|
|
45
|
+
const appUi = useDashboardUi();
|
|
46
|
+
const variants = {
|
|
47
|
+
divided: {
|
|
48
|
+
item: "bg-default px-4 py-1",
|
|
49
|
+
root: "-mx-4 gap-px bg-[var(--nut-dash-grid)]"
|
|
50
|
+
},
|
|
51
|
+
plain: { item: "", root: "gap-x-6 gap-y-5" },
|
|
52
|
+
tiles: { item: "rounded-lg border border-default p-3.5", root: "gap-3" }
|
|
53
|
+
};
|
|
54
|
+
const classes = computed(
|
|
55
|
+
() => resolveDashboardClasses(
|
|
56
|
+
{
|
|
57
|
+
caption: "min-w-0",
|
|
58
|
+
delta: "shrink-0 font-semibold tabular-nums",
|
|
59
|
+
icon: "grid size-7 shrink-0 place-items-center rounded-md",
|
|
60
|
+
item: `flex min-w-0 flex-col ${variants[variant].item}`,
|
|
61
|
+
label: "min-w-0 truncate text-xs text-muted",
|
|
62
|
+
meta: "mt-1 flex min-w-0 flex-wrap items-baseline gap-x-1.5 text-[11.5px] text-muted",
|
|
63
|
+
progress: "mt-2.5 h-1.5 overflow-hidden rounded-full bg-[var(--nut-dash-track)]",
|
|
64
|
+
grid: `grid ${variants[variant].root}`,
|
|
65
|
+
status: "inline-flex shrink-0 items-center rounded-full px-1.5 py-px text-[10.5px] font-semibold",
|
|
66
|
+
value: "mt-1.5 flex min-w-0 items-center gap-2 text-xl leading-tight font-semibold tracking-tight text-highlighted tabular-nums"
|
|
67
|
+
},
|
|
68
|
+
appUi.value.stats,
|
|
69
|
+
ui
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
const columnCount = computed(() => {
|
|
73
|
+
if (columns) return columns;
|
|
74
|
+
const count = Math.max(1, Math.min(items.length, 4));
|
|
75
|
+
return count > 2 ? `2 md:${count}` : String(count);
|
|
76
|
+
});
|
|
77
|
+
const gridColumns = useResponsiveValue(() => columnCount.value, "grid-cols");
|
|
78
|
+
const entries = computed(() => {
|
|
79
|
+
const data = source.data;
|
|
80
|
+
if (data === void 0) return [];
|
|
81
|
+
return items.map((item, index) => {
|
|
82
|
+
const raw = item.value(data);
|
|
83
|
+
const amount = item.delta?.(data);
|
|
84
|
+
const done = item.progress?.(data);
|
|
85
|
+
const status = item.status?.(data);
|
|
86
|
+
const caption = item.caption === void 0 || isString(item.caption) || isNumber(item.caption) ? item.caption : item.caption(data);
|
|
87
|
+
const good = isNumber(amount) ? item.invertDelta ? amount <= 0 : amount >= 0 : false;
|
|
88
|
+
return {
|
|
89
|
+
caption: resolveTextValue(caption),
|
|
90
|
+
change: isNumber(amount) && Number.isFinite(amount) ? amount : null,
|
|
91
|
+
color: resolveDashboardColor(item.color, index),
|
|
92
|
+
delta: isNumber(amount) && Number.isFinite(amount) ? { good, label: formats.delta.value(amount), up: amount >= 0 } : null,
|
|
93
|
+
icon: item.icon,
|
|
94
|
+
key: item.key,
|
|
95
|
+
label: resolveTextValue(item.label),
|
|
96
|
+
progress: isNumber(done) ? Math.round(Math.max(0, Math.min(100, done))) : null,
|
|
97
|
+
raw,
|
|
98
|
+
status: status ? { color: resolveDashboardColor(status.color, 0), label: resolveTextValue(status.label) } : null,
|
|
99
|
+
value: raw === null || raw === void 0 ? "\u2013" : isNumber(raw) ? (item.format ?? formats.number.value)(raw) : resolveTextValue(raw)
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
function tabulate() {
|
|
104
|
+
const hasDelta = items.some((item) => item.delta);
|
|
105
|
+
return {
|
|
106
|
+
columns: [
|
|
107
|
+
{ key: "label", label: t("dashboard.table.label"), numeric: false },
|
|
108
|
+
{ key: "value", label: t("dashboard.table.value"), numeric: true },
|
|
109
|
+
...hasDelta ? [{ key: "change", label: t("dashboard.table.change"), numeric: true }] : []
|
|
110
|
+
],
|
|
111
|
+
rows: entries.value.map((entry) => [
|
|
112
|
+
toDashboardCell(entry.label),
|
|
113
|
+
toDashboardCell(isNumber(entry.raw) ? entry.raw : entry.value, entry.value),
|
|
114
|
+
...hasDelta ? [toDashboardCell(entry.change, entry.delta?.label ?? "")] : []
|
|
115
|
+
])
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<DashboardCard
|
|
122
|
+
v-bind="block"
|
|
123
|
+
:card
|
|
124
|
+
:menu
|
|
125
|
+
:freshness
|
|
126
|
+
:ui
|
|
127
|
+
:source
|
|
128
|
+
:is-empty="entries.length === 0"
|
|
129
|
+
:tabulate
|
|
130
|
+
>
|
|
131
|
+
<template #skeleton>
|
|
132
|
+
<DashboardSkeleton kind="stats" :count="items.length" />
|
|
133
|
+
</template>
|
|
134
|
+
<template v-if="$slots['header-right']" #header-right>
|
|
135
|
+
<slot name="header-right" />
|
|
136
|
+
</template>
|
|
137
|
+
<template v-if="$slots.toolbar" #toolbar>
|
|
138
|
+
<slot name="toolbar" />
|
|
139
|
+
</template>
|
|
140
|
+
<template v-if="$slots.footer" #footer>
|
|
141
|
+
<slot name="footer" />
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<div :class="classes.grid" :style="gridColumns ?? void 0" :data-variant="variant">
|
|
145
|
+
<div v-for="entry in entries" :key="entry.key" :class="classes.item" data-stats-item>
|
|
146
|
+
<div class="flex min-w-0 items-center gap-2">
|
|
147
|
+
<span
|
|
148
|
+
v-if="entry.icon"
|
|
149
|
+
:class="classes.icon"
|
|
150
|
+
:style="{
|
|
151
|
+
background: `color-mix(in oklab, ${entry.color} 12%, transparent)`,
|
|
152
|
+
color: entry.color
|
|
153
|
+
}"
|
|
154
|
+
>
|
|
155
|
+
<UIcon :name="entry.icon" class="size-3.5" />
|
|
156
|
+
</span>
|
|
157
|
+
<span :class="classes.label">{{ entry.label }}</span>
|
|
158
|
+
</div>
|
|
159
|
+
<div :class="classes.value">
|
|
160
|
+
<span class="truncate">{{ entry.value }}</span>
|
|
161
|
+
<span
|
|
162
|
+
v-if="entry.status"
|
|
163
|
+
:class="classes.status"
|
|
164
|
+
:style="{
|
|
165
|
+
background: `color-mix(in oklab, ${entry.status.color} 14%, transparent)`,
|
|
166
|
+
color: entry.status.color
|
|
167
|
+
}"
|
|
168
|
+
>
|
|
169
|
+
{{ entry.status.label }}
|
|
170
|
+
</span>
|
|
171
|
+
</div>
|
|
172
|
+
<div v-if="entry.delta || entry.caption" :class="classes.meta">
|
|
173
|
+
<span
|
|
174
|
+
v-if="entry.delta"
|
|
175
|
+
:data-trend="entry.delta.good ? 'up' : 'down'"
|
|
176
|
+
:class="[
|
|
177
|
+
classes.delta,
|
|
178
|
+
entry.delta.good ? 'text-[var(--nut-dash-up)]' : 'text-[var(--nut-dash-down)]'
|
|
179
|
+
]"
|
|
180
|
+
>
|
|
181
|
+
{{ entry.delta.up ? "\u25B2" : "\u25BC" }} {{ entry.delta.label }}
|
|
182
|
+
</span>
|
|
183
|
+
<span v-if="entry.caption" :class="classes.caption">{{ entry.caption }}</span>
|
|
184
|
+
</div>
|
|
185
|
+
<div
|
|
186
|
+
v-if="entry.progress !== null"
|
|
187
|
+
role="progressbar"
|
|
188
|
+
:aria-valuenow="entry.progress"
|
|
189
|
+
aria-valuemin="0"
|
|
190
|
+
aria-valuemax="100"
|
|
191
|
+
:aria-label="entry.label"
|
|
192
|
+
:class="classes.progress"
|
|
193
|
+
>
|
|
194
|
+
<i
|
|
195
|
+
class="block h-full rounded-full transition-[width] duration-500 ease-out"
|
|
196
|
+
:style="{ background: entry.color, width: `${entry.progress}%` }"
|
|
197
|
+
/>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</DashboardCard>
|
|
202
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DashboardBlockBaseProps, DashboardBlockUi, DashboardSourceLike, DashboardStatsItem, DashboardStatsUi } from '../types/index.js';
|
|
2
|
+
declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<Omit<DashboardBlockBaseProps, "ui"> & {
|
|
4
|
+
source: DashboardSourceLike<TData>;
|
|
5
|
+
/** Figures read from the source data, in display order. */
|
|
6
|
+
items: readonly DashboardStatsItem<TData & ({} | null)>[];
|
|
7
|
+
/**
|
|
8
|
+
* Column count, responsive: `"2 md:4"`. Defaults to one column per figure (up to 4), two on
|
|
9
|
+
* small screens.
|
|
10
|
+
*/
|
|
11
|
+
columns?: string;
|
|
12
|
+
/**
|
|
13
|
+
* `plain`: figures side by side. `divided`: 1px rules between them. `tiles`: each figure in its
|
|
14
|
+
* own bordered box.
|
|
15
|
+
*/
|
|
16
|
+
variant?: "plain" | "divided" | "tiles";
|
|
17
|
+
ui?: DashboardBlockUi & DashboardStatsUi;
|
|
18
|
+
}> & (typeof globalThis extends {
|
|
19
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
20
|
+
} ? P : {});
|
|
21
|
+
expose: (exposed: {}) => void;
|
|
22
|
+
attrs: any;
|
|
23
|
+
slots: {
|
|
24
|
+
'header-right'?: () => unknown;
|
|
25
|
+
toolbar?: () => unknown;
|
|
26
|
+
footer?: () => unknown;
|
|
27
|
+
};
|
|
28
|
+
emit: {};
|
|
29
|
+
}>) => import("vue").VNode & {
|
|
30
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
31
|
+
};
|
|
32
|
+
declare const _default: typeof __VLS_export;
|
|
33
|
+
export default _default;
|
|
34
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
35
|
+
[K in keyof T]: T[K];
|
|
36
|
+
} : {
|
|
37
|
+
[K in keyof T as K]: T[K];
|
|
38
|
+
}) & {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DashboardBlockBaseProps, DashboardBlockUi, DashboardRowActions, DashboardSelected, DashboardSelectEvent, DashboardSourceLike, DashboardTableColumn, DashboardTableSort, DashboardTableUi } from '../types/index.js';
|
|
2
|
+
declare const __VLS_export: <TRow>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<(Omit<DashboardBlockBaseProps, "ui"> & {
|
|
4
|
+
source: DashboardSourceLike<readonly TRow[] | undefined>;
|
|
5
|
+
columns: readonly DashboardTableColumn<TRow>[];
|
|
6
|
+
/** Rows shown in the card, after sorting. The expand dialog and CSV export show them all. */
|
|
7
|
+
limit?: number;
|
|
8
|
+
/** Caps the table height in pixels; the header stays visible while the rows scroll. */
|
|
9
|
+
maxHeight?: number;
|
|
10
|
+
rowKey?: (row: TRow, index: number) => PropertyKey;
|
|
11
|
+
/** Rows shown as selected (the value a `select` handler stored). */
|
|
12
|
+
selected?: DashboardSelected<TRow>;
|
|
13
|
+
/** Actions of each row, in a last column: inline icon buttons and a `⋮` menu. */
|
|
14
|
+
rowActions?: DashboardRowActions<TRow>;
|
|
15
|
+
/** Makes each row a button. */
|
|
16
|
+
onSelect?: (event: DashboardSelectEvent<TRow>) => void;
|
|
17
|
+
ui?: DashboardBlockUi & DashboardTableUi;
|
|
18
|
+
} & {
|
|
19
|
+
/**
|
|
20
|
+
* Sort state. Header clicks cycle a column through its natural direction (numbers descending, text
|
|
21
|
+
* ascending), the opposite one, then source order. Bind it to keep the sort in a param.
|
|
22
|
+
*/
|
|
23
|
+
sort?: DashboardTableSort | null | undefined;
|
|
24
|
+
}) & {
|
|
25
|
+
"onUpdate:sort"?: ((value: DashboardTableSort | null | undefined) => any) | undefined;
|
|
26
|
+
}> & (typeof globalThis extends {
|
|
27
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
28
|
+
} ? P : {});
|
|
29
|
+
expose: (exposed: {}) => void;
|
|
30
|
+
attrs: any;
|
|
31
|
+
slots: {
|
|
32
|
+
'header-right'?: () => unknown;
|
|
33
|
+
toolbar?: () => unknown;
|
|
34
|
+
footer?: () => unknown;
|
|
35
|
+
} & {
|
|
36
|
+
[slot: `cell-${string}`]: ((props: {
|
|
37
|
+
row: TRow;
|
|
38
|
+
index: number;
|
|
39
|
+
value: string;
|
|
40
|
+
}) => unknown) | undefined;
|
|
41
|
+
};
|
|
42
|
+
emit: (event: "update:sort", value: DashboardTableSort | null | undefined) => void;
|
|
43
|
+
}>) => import("vue").VNode & {
|
|
44
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
45
|
+
};
|
|
46
|
+
declare const _default: typeof __VLS_export;
|
|
47
|
+
export default _default;
|
|
48
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
49
|
+
[K in keyof T]: T[K];
|
|
50
|
+
} : {
|
|
51
|
+
[K in keyof T as K]: T[K];
|
|
52
|
+
}) & {};
|