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,187 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
4
|
+
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
5
|
+
import { useDashboardFormat } from "../composables/use-dashboard-format";
|
|
6
|
+
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
7
|
+
import { fadeDashboardColor, resolveDashboardColor } from "../utils/charts";
|
|
8
|
+
import { toDashboardCell } from "../utils/export";
|
|
9
|
+
import {
|
|
10
|
+
DASHBOARD_ROW_BUTTON,
|
|
11
|
+
DASHBOARD_SELECTABLE_ROW,
|
|
12
|
+
DASHBOARD_SELECTED_ROW,
|
|
13
|
+
resolveDashboardClasses
|
|
14
|
+
} from "../utils/ui";
|
|
15
|
+
import DashboardRowActionsMenu from "./block/dashboard-row-actions.vue";
|
|
16
|
+
import DashboardSkeleton from "./block/dashboard-skeleton.vue";
|
|
17
|
+
import DashboardCard from "./dashboard-card.vue";
|
|
18
|
+
const {
|
|
19
|
+
source,
|
|
20
|
+
card = true,
|
|
21
|
+
menu = void 0,
|
|
22
|
+
freshness = void 0,
|
|
23
|
+
label,
|
|
24
|
+
tag,
|
|
25
|
+
value,
|
|
26
|
+
meta,
|
|
27
|
+
format,
|
|
28
|
+
max,
|
|
29
|
+
color,
|
|
30
|
+
emphasis = "all",
|
|
31
|
+
limit,
|
|
32
|
+
rowKey,
|
|
33
|
+
selected,
|
|
34
|
+
rowActions,
|
|
35
|
+
onSelect,
|
|
36
|
+
ui,
|
|
37
|
+
...block
|
|
38
|
+
} = defineProps({
|
|
39
|
+
title: { type: [String, Number, Function], required: false },
|
|
40
|
+
subtitle: { type: [String, Number, Function], required: false },
|
|
41
|
+
size: { type: String, required: false },
|
|
42
|
+
rows: { type: String, required: false },
|
|
43
|
+
card: { type: Boolean, required: false },
|
|
44
|
+
activation: { type: String, required: false },
|
|
45
|
+
empty: { type: Object, required: false },
|
|
46
|
+
menu: { type: [Boolean, Array, Function], required: false },
|
|
47
|
+
actions: { type: Array, required: false },
|
|
48
|
+
freshness: { type: Boolean, required: false },
|
|
49
|
+
source: { type: Object, required: true },
|
|
50
|
+
label: { type: Function, required: true },
|
|
51
|
+
tag: { type: Function, required: false },
|
|
52
|
+
value: { type: Function, required: true },
|
|
53
|
+
meta: { type: Function, required: false },
|
|
54
|
+
format: { type: Function, required: false },
|
|
55
|
+
max: { type: Number, required: false },
|
|
56
|
+
color: { type: String, required: false },
|
|
57
|
+
emphasis: { type: String, required: false },
|
|
58
|
+
limit: { type: Number, required: false },
|
|
59
|
+
rowKey: { type: Function, required: false },
|
|
60
|
+
selected: { type: Function, required: false },
|
|
61
|
+
rowActions: { type: Function, required: false },
|
|
62
|
+
onSelect: { type: Function, required: false },
|
|
63
|
+
ui: { type: Object, required: false }
|
|
64
|
+
});
|
|
65
|
+
defineSlots();
|
|
66
|
+
const { t } = useUiToolsLocale();
|
|
67
|
+
const formats = useDashboardFormat();
|
|
68
|
+
const appUi = useDashboardUi();
|
|
69
|
+
const classes = computed(
|
|
70
|
+
() => resolveDashboardClasses(
|
|
71
|
+
{
|
|
72
|
+
fill: "block h-full rounded-full transition-[width] duration-500 ease-out",
|
|
73
|
+
label: "min-w-0 truncate font-medium text-default",
|
|
74
|
+
meta: "shrink-0 text-xs text-muted tabular-nums",
|
|
75
|
+
row: "flex flex-col gap-[5px] py-1.5 text-[13px]",
|
|
76
|
+
tag: "ms-1.5 font-mono text-[11px] font-normal text-dimmed",
|
|
77
|
+
track: "h-1.5 overflow-hidden rounded-full bg-[var(--nut-dash-track)]"
|
|
78
|
+
},
|
|
79
|
+
appUi.value.bars,
|
|
80
|
+
ui
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
const fill = computed(() => resolveDashboardColor(color, 0));
|
|
84
|
+
const entries = computed(
|
|
85
|
+
() => (source.data ?? []).map((row, index) => {
|
|
86
|
+
const amount = value(row);
|
|
87
|
+
return {
|
|
88
|
+
actions: rowActions?.(row, index) ?? [],
|
|
89
|
+
amount,
|
|
90
|
+
index,
|
|
91
|
+
key: rowKey?.(row, index) ?? index,
|
|
92
|
+
label: resolveTextValue(label(row)),
|
|
93
|
+
meta: meta ? resolveTextValue(meta(row)) : (format ?? formats.number.value)(amount),
|
|
94
|
+
row,
|
|
95
|
+
selected: selected?.(row, index) ?? false,
|
|
96
|
+
tag: tag ? resolveTextValue(tag(row)) : ""
|
|
97
|
+
};
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
const anySelected = computed(() => entries.value.some((entry) => entry.selected));
|
|
101
|
+
function visible(expanded) {
|
|
102
|
+
const shown = limit && !expanded ? entries.value.slice(0, limit) : entries.value;
|
|
103
|
+
const full = max ?? Math.max(0, ...shown.map((entry) => entry.amount));
|
|
104
|
+
return shown.map((entry) => ({
|
|
105
|
+
...entry,
|
|
106
|
+
color: anySelected.value ? entry.selected ? fill.value : fadeDashboardColor(fill.value) : emphasis === "first" && entry.index > 0 ? "var(--nut-dash-muted)" : fill.value,
|
|
107
|
+
width: full > 0 ? `${Math.max(0, Math.min(100, entry.amount / full * 100))}%` : "0%"
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
function tabulate() {
|
|
111
|
+
return {
|
|
112
|
+
columns: [
|
|
113
|
+
{ key: "label", label: t("dashboard.table.label"), numeric: false },
|
|
114
|
+
{ key: "value", label: t("dashboard.table.value"), numeric: true }
|
|
115
|
+
],
|
|
116
|
+
rows: entries.value.map((entry) => [
|
|
117
|
+
toDashboardCell(entry.tag ? `${entry.label} ${entry.tag}` : entry.label),
|
|
118
|
+
toDashboardCell(entry.amount, (format ?? formats.number.value)(entry.amount))
|
|
119
|
+
])
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<template>
|
|
125
|
+
<DashboardCard
|
|
126
|
+
v-bind="block"
|
|
127
|
+
:card
|
|
128
|
+
:menu
|
|
129
|
+
:freshness
|
|
130
|
+
:ui
|
|
131
|
+
:source
|
|
132
|
+
:is-empty="entries.length === 0"
|
|
133
|
+
:tabulate
|
|
134
|
+
>
|
|
135
|
+
<template #skeleton>
|
|
136
|
+
<DashboardSkeleton kind="hbars" :count="limit ?? 5" />
|
|
137
|
+
</template>
|
|
138
|
+
<template v-if="$slots['header-right']" #header-right>
|
|
139
|
+
<slot name="header-right" />
|
|
140
|
+
</template>
|
|
141
|
+
<template v-if="$slots.toolbar" #toolbar>
|
|
142
|
+
<slot name="toolbar" />
|
|
143
|
+
</template>
|
|
144
|
+
<template v-if="$slots.footer" #footer>
|
|
145
|
+
<slot name="footer" />
|
|
146
|
+
</template>
|
|
147
|
+
|
|
148
|
+
<template #default="{ expanded }">
|
|
149
|
+
<ul class="flex flex-col">
|
|
150
|
+
<li
|
|
151
|
+
v-for="entry in visible(expanded)"
|
|
152
|
+
:key="entry.key"
|
|
153
|
+
:data-selected="entry.selected || void 0"
|
|
154
|
+
:class="[
|
|
155
|
+
classes.row,
|
|
156
|
+
onSelect && DASHBOARD_SELECTABLE_ROW,
|
|
157
|
+
entry.selected && DASHBOARD_SELECTED_ROW
|
|
158
|
+
]"
|
|
159
|
+
>
|
|
160
|
+
<div class="flex items-baseline justify-between gap-2.5">
|
|
161
|
+
<span :class="classes.label">
|
|
162
|
+
{{ entry.label }}<small v-if="entry.tag" :class="classes.tag">{{ entry.tag }}</small>
|
|
163
|
+
</span>
|
|
164
|
+
<span :class="classes.meta">{{ entry.meta }}</span>
|
|
165
|
+
<DashboardRowActionsMenu
|
|
166
|
+
v-if="entry.actions.length"
|
|
167
|
+
:actions="entry.actions"
|
|
168
|
+
:label="entry.label"
|
|
169
|
+
class="-my-1.5 self-center"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
<div :class="classes.track">
|
|
173
|
+
<i :class="classes.fill" :style="{ background: entry.color, width: entry.width }" />
|
|
174
|
+
</div>
|
|
175
|
+
<button
|
|
176
|
+
v-if="onSelect"
|
|
177
|
+
type="button"
|
|
178
|
+
:class="DASHBOARD_ROW_BUTTON"
|
|
179
|
+
:aria-label="entry.label"
|
|
180
|
+
:aria-pressed="selected ? entry.selected : void 0"
|
|
181
|
+
@click="onSelect({ index: entry.index, row: entry.row })"
|
|
182
|
+
/>
|
|
183
|
+
</li>
|
|
184
|
+
</ul>
|
|
185
|
+
</template>
|
|
186
|
+
</DashboardCard>
|
|
187
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { LazyTextValue } from '#ui-tools/shared/types/utils';
|
|
2
|
+
import type { DashboardBarsUi, DashboardBlockBaseProps, DashboardBlockUi, DashboardRowActions, DashboardSelected, DashboardSelectEvent, DashboardSeriesColor, DashboardSourceLike, DashboardValueFormat } from '../types/index.js';
|
|
3
|
+
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<{
|
|
4
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<Omit<DashboardBlockBaseProps, "ui"> & {
|
|
5
|
+
source: DashboardSourceLike<readonly TRow[] | undefined>;
|
|
6
|
+
label: (row: TRow) => LazyTextValue;
|
|
7
|
+
/** Small monospace tag after the label (version, code…). */
|
|
8
|
+
tag?: (row: TRow) => LazyTextValue | undefined;
|
|
9
|
+
/** Bar length. */
|
|
10
|
+
value: (row: TRow) => number;
|
|
11
|
+
/** Right-aligned text. Defaults to the formatted value. */
|
|
12
|
+
meta?: (row: TRow) => LazyTextValue;
|
|
13
|
+
format?: DashboardValueFormat;
|
|
14
|
+
/** Value of a full bar. Defaults to the largest value. */
|
|
15
|
+
max?: number;
|
|
16
|
+
color?: DashboardSeriesColor;
|
|
17
|
+
/** `first`: only the leading row takes `color`, the others use the muted tone. */
|
|
18
|
+
emphasis?: "all" | "first";
|
|
19
|
+
/** Rows shown in the card. The expand dialog, table view, and CSV export show them all. */
|
|
20
|
+
limit?: number;
|
|
21
|
+
rowKey?: (row: TRow, index: number) => PropertyKey;
|
|
22
|
+
/** Rows shown as selected: their bars keep `color`, the others fade. */
|
|
23
|
+
selected?: DashboardSelected<TRow>;
|
|
24
|
+
/** Actions of each row: inline icon buttons and a `⋮` menu after the value. */
|
|
25
|
+
rowActions?: DashboardRowActions<TRow>;
|
|
26
|
+
/** Makes each row a button. */
|
|
27
|
+
onSelect?: (event: DashboardSelectEvent<TRow>) => void;
|
|
28
|
+
ui?: DashboardBlockUi & DashboardBarsUi;
|
|
29
|
+
}> & (typeof globalThis extends {
|
|
30
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
31
|
+
} ? P : {});
|
|
32
|
+
expose: (exposed: {}) => void;
|
|
33
|
+
attrs: any;
|
|
34
|
+
slots: {
|
|
35
|
+
'header-right'?: () => unknown;
|
|
36
|
+
toolbar?: () => unknown;
|
|
37
|
+
footer?: () => unknown;
|
|
38
|
+
};
|
|
39
|
+
emit: {};
|
|
40
|
+
}>) => import("vue").VNode & {
|
|
41
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
42
|
+
};
|
|
43
|
+
declare const _default: typeof __VLS_export;
|
|
44
|
+
export default _default;
|
|
45
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
46
|
+
[K in keyof T]: T[K];
|
|
47
|
+
} : {
|
|
48
|
+
[K in keyof T as K]: T[K];
|
|
49
|
+
}) & {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DashboardBlockBaseProps, DashboardDataTable, DashboardLegendItem, DashboardMenu, DashboardSkeletonKind, DashboardSourceLike } from '../types/index.js';
|
|
2
|
+
type __VLS_Props = DashboardBlockBaseProps & {
|
|
3
|
+
/** Source whose state drives the card: skeleton, error with retry, empty, refresh bar. */
|
|
4
|
+
source?: DashboardSourceLike;
|
|
5
|
+
/** Whether the ready source has nothing to show. Blocks compute it from their typed data. */
|
|
6
|
+
isEmpty?: boolean;
|
|
7
|
+
legend?: readonly DashboardLegendItem[];
|
|
8
|
+
/** Skeleton drawn while the source loads when no `#skeleton` slot is given. */
|
|
9
|
+
skeleton?: DashboardSkeletonKind;
|
|
10
|
+
/**
|
|
11
|
+
* Tabular data behind the `table` and `csv` menu actions. Called only when one of them runs,
|
|
12
|
+
* so it can read the source freely.
|
|
13
|
+
*/
|
|
14
|
+
tabulate?: () => DashboardDataTable;
|
|
15
|
+
/** Offers the `table` menu action when `tabulate` is set. Off for blocks that are tables. */
|
|
16
|
+
viewAsTable?: boolean;
|
|
17
|
+
/** Offers the `expand` menu action. The default slot renders again, with `expanded`. */
|
|
18
|
+
expandable?: boolean;
|
|
19
|
+
};
|
|
20
|
+
type __VLS_Slots = {
|
|
21
|
+
/** Content. Rendered a second time inside the expand dialog, with `expanded: true`. */
|
|
22
|
+
default?: (props: {
|
|
23
|
+
expanded: boolean;
|
|
24
|
+
}) => unknown;
|
|
25
|
+
skeleton?: () => unknown;
|
|
26
|
+
'header-right'?: () => unknown;
|
|
27
|
+
/** Row under the header (active filters, chips…), kept visible in every phase. */
|
|
28
|
+
toolbar?: () => unknown;
|
|
29
|
+
footer?: () => unknown;
|
|
30
|
+
};
|
|
31
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
32
|
+
menu: DashboardMenu;
|
|
33
|
+
card: boolean;
|
|
34
|
+
freshness: boolean;
|
|
35
|
+
isEmpty: boolean;
|
|
36
|
+
skeleton: DashboardSkeletonKind;
|
|
37
|
+
viewAsTable: boolean;
|
|
38
|
+
expandable: boolean;
|
|
39
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
40
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
41
|
+
declare const _default: typeof __VLS_export;
|
|
42
|
+
export default _default;
|
|
43
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
44
|
+
new (): {
|
|
45
|
+
$slots: S;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UButton from "@nuxt/ui/components/Button.vue";
|
|
3
|
+
import UDropdownMenu from "@nuxt/ui/components/DropdownMenu.vue";
|
|
4
|
+
import UModal from "@nuxt/ui/components/Modal.vue";
|
|
5
|
+
import { twMerge } from "tailwind-merge";
|
|
6
|
+
import { computed, shallowRef, useTemplateRef } from "vue";
|
|
7
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
8
|
+
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
9
|
+
import { useDashboardBlock } from "../composables/use-dashboard-block";
|
|
10
|
+
import { useDashboardTime } from "../composables/use-dashboard-time";
|
|
11
|
+
import { useDashboardGridContext, useDashboardUi } from "../composables/use-dashboard-ui";
|
|
12
|
+
import { downloadDashboardFile, resolveDashboardFileName, toDashboardCsv } from "../utils/export";
|
|
13
|
+
import { resolveDashboardClasses } from "../utils/ui";
|
|
14
|
+
import DashboardBlockState from "./block/dashboard-block-state.vue";
|
|
15
|
+
import DashboardDataTableView from "./block/dashboard-data-table.vue";
|
|
16
|
+
import DashboardSkeleton from "./block/dashboard-skeleton.vue";
|
|
17
|
+
import DashboardLegend from "./dashboard-legend.vue";
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
title: { type: [String, Number, Function], required: false },
|
|
20
|
+
subtitle: { type: [String, Number, Function], required: false },
|
|
21
|
+
size: { type: String, required: false },
|
|
22
|
+
rows: { type: String, required: false },
|
|
23
|
+
card: { type: Boolean, required: false, default: true },
|
|
24
|
+
activation: { type: String, required: false },
|
|
25
|
+
empty: { type: Object, required: false },
|
|
26
|
+
menu: { type: [Boolean, Array, Function], required: false, default: void 0 },
|
|
27
|
+
actions: { type: Array, required: false },
|
|
28
|
+
freshness: { type: Boolean, required: false, default: void 0 },
|
|
29
|
+
ui: { type: Object, required: false },
|
|
30
|
+
source: { type: Object, required: false },
|
|
31
|
+
isEmpty: { type: Boolean, required: false, default: false },
|
|
32
|
+
legend: { type: Array, required: false },
|
|
33
|
+
skeleton: { type: String, required: false, default: "rows" },
|
|
34
|
+
tabulate: { type: Function, required: false },
|
|
35
|
+
viewAsTable: { type: Boolean, required: false, default: true },
|
|
36
|
+
expandable: { type: Boolean, required: false, default: true }
|
|
37
|
+
});
|
|
38
|
+
const slots = defineSlots();
|
|
39
|
+
const { t, code } = useUiToolsLocale();
|
|
40
|
+
const time = useDashboardTime();
|
|
41
|
+
const root = useTemplateRef("root");
|
|
42
|
+
const block = useDashboardBlock({
|
|
43
|
+
activation: () => props.activation,
|
|
44
|
+
empty: () => props.isEmpty,
|
|
45
|
+
root,
|
|
46
|
+
rows: () => props.rows,
|
|
47
|
+
size: () => props.size,
|
|
48
|
+
source: () => props.source
|
|
49
|
+
});
|
|
50
|
+
const appUi = useDashboardUi();
|
|
51
|
+
const grid = useDashboardGridContext();
|
|
52
|
+
const panel = computed(() => grid?.panels.value ?? false);
|
|
53
|
+
const classes = computed(() => {
|
|
54
|
+
const app = appUi.value;
|
|
55
|
+
const parts = resolveDashboardClasses(
|
|
56
|
+
{
|
|
57
|
+
actions: "ms-auto flex max-w-full shrink-0 flex-wrap items-center justify-end gap-x-3.5 gap-y-1.5",
|
|
58
|
+
// A column, so content that wants the card's full height (a centered gauge) can take it.
|
|
59
|
+
body: "flex min-w-0 flex-1 flex-col",
|
|
60
|
+
footer: "mt-3 flex flex-col",
|
|
61
|
+
footerActions: "mt-4 flex flex-wrap gap-2 *:flex-1 *:justify-center",
|
|
62
|
+
freshness: "mt-3 text-[11px] text-dimmed",
|
|
63
|
+
header: "mb-3.5 flex min-w-0 items-start gap-x-3",
|
|
64
|
+
// Centered on the 20px first line: the 24px button overhangs 2px above and below.
|
|
65
|
+
menu: "-my-0.5 -me-1.5 shrink-0 text-dimmed",
|
|
66
|
+
root: "",
|
|
67
|
+
subtitle: "max-w-full truncate text-xs font-normal tracking-normal text-muted",
|
|
68
|
+
// A subtitle that does not fit goes under the title rather than cutting it.
|
|
69
|
+
title: "flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5 text-sm font-semibold tracking-[-0.01em] text-highlighted",
|
|
70
|
+
toolbar: "-mt-1 mb-3.5 flex min-w-0 flex-wrap items-center gap-1.5"
|
|
71
|
+
},
|
|
72
|
+
app.card,
|
|
73
|
+
props.ui
|
|
74
|
+
);
|
|
75
|
+
parts.root = twMerge(
|
|
76
|
+
"relative flex min-w-0 flex-col",
|
|
77
|
+
props.card && ["rounded-lg border border-default bg-default px-6 py-5.5", app.card?.root],
|
|
78
|
+
props.card && panel.value && ["rounded-none border-0 shadow-none", app.grid?.panel],
|
|
79
|
+
props.ui?.root
|
|
80
|
+
);
|
|
81
|
+
return parts;
|
|
82
|
+
});
|
|
83
|
+
const title = computed(() => resolveTextValue(props.title));
|
|
84
|
+
const subtitle = computed(() => resolveTextValue(props.subtitle));
|
|
85
|
+
const phase = computed(() => block.phase.value);
|
|
86
|
+
const pending = computed(() => phase.value === "loading" || phase.value === "idle");
|
|
87
|
+
const busy = computed(() => phase.value === "loading" || block.refreshing.value);
|
|
88
|
+
const builtInActions = ["table", "csv", "expand"];
|
|
89
|
+
const tableView = shallowRef(false);
|
|
90
|
+
const expanded = shallowRef(false);
|
|
91
|
+
const expandedOnce = shallowRef(false);
|
|
92
|
+
function expand() {
|
|
93
|
+
expandedOnce.value = true;
|
|
94
|
+
expanded.value = true;
|
|
95
|
+
}
|
|
96
|
+
const menuContext = {
|
|
97
|
+
download,
|
|
98
|
+
expand,
|
|
99
|
+
table: () => phase.value === "content" ? props.tabulate?.() : void 0,
|
|
100
|
+
get title() {
|
|
101
|
+
return title.value;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const menuItems = computed(() => {
|
|
105
|
+
const menu = props.menu ?? grid?.menu.value ?? false;
|
|
106
|
+
if (menu === false) return [];
|
|
107
|
+
const entries = menu === true ? builtInActions : isMenuEntries(menu) ? menu : menu(menuContext);
|
|
108
|
+
return entries.flatMap((entry) => isMenuAction(entry) ? builtInItem(entry) : [entry]);
|
|
109
|
+
});
|
|
110
|
+
const actionButtons = computed(() => {
|
|
111
|
+
const header = [];
|
|
112
|
+
const footer = [];
|
|
113
|
+
for (const { placement, ...button } of props.actions ?? []) {
|
|
114
|
+
if (placement === "footer") footer.push(button);
|
|
115
|
+
else header.push(button);
|
|
116
|
+
}
|
|
117
|
+
return { footer, header };
|
|
118
|
+
});
|
|
119
|
+
const showTable = computed(
|
|
120
|
+
() => tableView.value && props.viewAsTable && phase.value === "content" && props.tabulate !== void 0
|
|
121
|
+
);
|
|
122
|
+
function isMenuEntries(menu) {
|
|
123
|
+
return Array.isArray(menu);
|
|
124
|
+
}
|
|
125
|
+
function isMenuAction(entry) {
|
|
126
|
+
return entry === "table" || entry === "csv" || entry === "expand";
|
|
127
|
+
}
|
|
128
|
+
function builtInItem(action) {
|
|
129
|
+
const disabled = phase.value !== "content";
|
|
130
|
+
if (action === "expand") {
|
|
131
|
+
if (!props.expandable) return [];
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
disabled,
|
|
135
|
+
icon: "i-lucide-maximize-2",
|
|
136
|
+
label: t("dashboard.menu.expand"),
|
|
137
|
+
onSelect: expand
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
if (!props.tabulate) return [];
|
|
142
|
+
if (action === "csv") {
|
|
143
|
+
return [
|
|
144
|
+
{
|
|
145
|
+
disabled,
|
|
146
|
+
icon: "i-lucide-download",
|
|
147
|
+
label: t("dashboard.menu.download"),
|
|
148
|
+
onSelect: download
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
if (!props.viewAsTable) return [];
|
|
153
|
+
return [
|
|
154
|
+
{
|
|
155
|
+
disabled,
|
|
156
|
+
icon: tableView.value ? "i-lucide-chart-no-axes-column" : "i-lucide-table-2",
|
|
157
|
+
label: t(tableView.value ? "dashboard.menu.hideTable" : "dashboard.menu.showTable"),
|
|
158
|
+
onSelect: () => {
|
|
159
|
+
tableView.value = !tableView.value;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
function download() {
|
|
165
|
+
if (!props.tabulate) return;
|
|
166
|
+
downloadDashboardFile(
|
|
167
|
+
resolveDashboardFileName(title.value, "csv"),
|
|
168
|
+
toDashboardCsv(props.tabulate(), code.value)
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
const updatedAt = computed(() => {
|
|
172
|
+
const freshness = props.freshness ?? grid?.freshness.value ?? false;
|
|
173
|
+
return freshness && phase.value === "content" ? props.source?.updatedAt : void 0;
|
|
174
|
+
});
|
|
175
|
+
const hasHeader = computed(
|
|
176
|
+
() => Boolean(
|
|
177
|
+
title.value || subtitle.value || props.legend?.length || slots["header-right"] || actionButtons.value.header.length || menuItems.value.length
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
<template>
|
|
183
|
+
<section
|
|
184
|
+
ref="root"
|
|
185
|
+
:style="block.style.value"
|
|
186
|
+
:data-phase="phase"
|
|
187
|
+
:data-panel="panel || void 0"
|
|
188
|
+
:aria-busy="busy"
|
|
189
|
+
:class="classes.root"
|
|
190
|
+
>
|
|
191
|
+
<div
|
|
192
|
+
v-if="block.refreshing.value"
|
|
193
|
+
aria-hidden="true"
|
|
194
|
+
class="pointer-events-none absolute inset-x-0 top-0 h-0.5 overflow-hidden"
|
|
195
|
+
>
|
|
196
|
+
<div class="nut-dash-progress h-full w-1/3 rounded-full bg-primary" />
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<!-- Title and actions share the first line and wrap as a group; the menu keeps the corner. -->
|
|
200
|
+
<header v-if="hasHeader" :class="classes.header">
|
|
201
|
+
<div class="flex min-w-0 flex-1 flex-wrap items-center gap-x-3 gap-y-2">
|
|
202
|
+
<h2 :class="classes.title">
|
|
203
|
+
<span class="max-w-full truncate">{{ title }}</span>
|
|
204
|
+
<small v-if="subtitle" :class="classes.subtitle">{{ subtitle }}</small>
|
|
205
|
+
</h2>
|
|
206
|
+
<div
|
|
207
|
+
v-if="
|
|
208
|
+
legend?.length && !showTable || $slots['header-right'] || actionButtons.header.length
|
|
209
|
+
"
|
|
210
|
+
:class="classes.actions"
|
|
211
|
+
>
|
|
212
|
+
<DashboardLegend v-if="legend?.length && !showTable" :items="legend" />
|
|
213
|
+
<slot name="header-right" />
|
|
214
|
+
<UButton
|
|
215
|
+
v-for="(button, index) in actionButtons.header"
|
|
216
|
+
:key="index"
|
|
217
|
+
color="neutral"
|
|
218
|
+
variant="outline"
|
|
219
|
+
size="xs"
|
|
220
|
+
v-bind="button"
|
|
221
|
+
data-dashboard-action
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
<UDropdownMenu
|
|
226
|
+
v-if="menuItems.length"
|
|
227
|
+
:items="menuItems"
|
|
228
|
+
:content="{ align: 'end', side: 'bottom', sideOffset: 6 }"
|
|
229
|
+
>
|
|
230
|
+
<UButton
|
|
231
|
+
color="neutral"
|
|
232
|
+
variant="ghost"
|
|
233
|
+
size="xs"
|
|
234
|
+
square
|
|
235
|
+
icon="i-lucide-ellipsis"
|
|
236
|
+
:aria-label="t('dashboard.menu.label')"
|
|
237
|
+
:class="classes.menu"
|
|
238
|
+
data-dashboard-menu
|
|
239
|
+
/>
|
|
240
|
+
</UDropdownMenu>
|
|
241
|
+
</header>
|
|
242
|
+
|
|
243
|
+
<div v-if="$slots.toolbar" :class="classes.toolbar">
|
|
244
|
+
<slot name="toolbar" />
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div :class="classes.body">
|
|
248
|
+
<div v-if="showTable && tabulate" class="nut-dash-enter">
|
|
249
|
+
<DashboardDataTableView :table="tabulate()" scroll />
|
|
250
|
+
</div>
|
|
251
|
+
<div v-else-if="phase === 'content'" class="nut-dash-enter flex-1">
|
|
252
|
+
<slot :expanded="false" />
|
|
253
|
+
</div>
|
|
254
|
+
<DashboardBlockState
|
|
255
|
+
v-else-if="phase === 'error' || phase === 'empty'"
|
|
256
|
+
:kind="phase"
|
|
257
|
+
:empty
|
|
258
|
+
@retry="block.retry"
|
|
259
|
+
/>
|
|
260
|
+
<div v-else aria-hidden="true" :class="phase === 'loading' && 'nut-dash-shimmer'">
|
|
261
|
+
<slot name="skeleton">
|
|
262
|
+
<DashboardSkeleton :kind="skeleton" />
|
|
263
|
+
</slot>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<footer v-if="$slots.footer && phase === 'content'" :class="classes.footer">
|
|
268
|
+
<slot name="footer" />
|
|
269
|
+
</footer>
|
|
270
|
+
<footer
|
|
271
|
+
v-else-if="$slots.footer && pending"
|
|
272
|
+
aria-hidden="true"
|
|
273
|
+
:class="[classes.footer, phase === 'loading' && 'nut-dash-shimmer']"
|
|
274
|
+
>
|
|
275
|
+
<div class="flex items-center justify-between border-t border-[var(--nut-dash-grid)] pt-3">
|
|
276
|
+
<div class="nut-dash-ghost h-2.5 w-24 rounded-sm opacity-70" />
|
|
277
|
+
<div class="nut-dash-ghost h-3 w-14 rounded-sm" />
|
|
278
|
+
</div>
|
|
279
|
+
</footer>
|
|
280
|
+
|
|
281
|
+
<p v-if="updatedAt !== void 0" :class="classes.freshness">
|
|
282
|
+
<time
|
|
283
|
+
:datetime="new Date(updatedAt).toISOString()"
|
|
284
|
+
:title="time.absolute(updatedAt)"
|
|
285
|
+
data-allow-mismatch="text"
|
|
286
|
+
>
|
|
287
|
+
{{ t("dashboard.freshness.updated", { time: time.relative(updatedAt) }) }}
|
|
288
|
+
</time>
|
|
289
|
+
</p>
|
|
290
|
+
|
|
291
|
+
<div v-if="actionButtons.footer.length" :class="classes.footerActions">
|
|
292
|
+
<UButton
|
|
293
|
+
v-for="(button, index) in actionButtons.footer"
|
|
294
|
+
:key="index"
|
|
295
|
+
color="neutral"
|
|
296
|
+
variant="outline"
|
|
297
|
+
v-bind="button"
|
|
298
|
+
data-dashboard-action
|
|
299
|
+
/>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<UModal
|
|
303
|
+
v-if="expandedOnce"
|
|
304
|
+
v-model:open="expanded"
|
|
305
|
+
:title="title || t('dashboard.menu.expand')"
|
|
306
|
+
:description="subtitle || void 0"
|
|
307
|
+
:ui="{ content: 'sm:max-w-5xl' }"
|
|
308
|
+
>
|
|
309
|
+
<template #body>
|
|
310
|
+
<DashboardLegend v-if="legend?.length && !showTable" :items="legend" class="mb-4" />
|
|
311
|
+
<DashboardDataTableView v-if="showTable && tabulate" :table="tabulate()" />
|
|
312
|
+
<slot v-else-if="phase === 'content'" :expanded="true" />
|
|
313
|
+
</template>
|
|
314
|
+
</UModal>
|
|
315
|
+
</section>
|
|
316
|
+
</template>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DashboardBlockBaseProps, DashboardDataTable, DashboardLegendItem, DashboardMenu, DashboardSkeletonKind, DashboardSourceLike } from '../types/index.js';
|
|
2
|
+
type __VLS_Props = DashboardBlockBaseProps & {
|
|
3
|
+
/** Source whose state drives the card: skeleton, error with retry, empty, refresh bar. */
|
|
4
|
+
source?: DashboardSourceLike;
|
|
5
|
+
/** Whether the ready source has nothing to show. Blocks compute it from their typed data. */
|
|
6
|
+
isEmpty?: boolean;
|
|
7
|
+
legend?: readonly DashboardLegendItem[];
|
|
8
|
+
/** Skeleton drawn while the source loads when no `#skeleton` slot is given. */
|
|
9
|
+
skeleton?: DashboardSkeletonKind;
|
|
10
|
+
/**
|
|
11
|
+
* Tabular data behind the `table` and `csv` menu actions. Called only when one of them runs,
|
|
12
|
+
* so it can read the source freely.
|
|
13
|
+
*/
|
|
14
|
+
tabulate?: () => DashboardDataTable;
|
|
15
|
+
/** Offers the `table` menu action when `tabulate` is set. Off for blocks that are tables. */
|
|
16
|
+
viewAsTable?: boolean;
|
|
17
|
+
/** Offers the `expand` menu action. The default slot renders again, with `expanded`. */
|
|
18
|
+
expandable?: boolean;
|
|
19
|
+
};
|
|
20
|
+
type __VLS_Slots = {
|
|
21
|
+
/** Content. Rendered a second time inside the expand dialog, with `expanded: true`. */
|
|
22
|
+
default?: (props: {
|
|
23
|
+
expanded: boolean;
|
|
24
|
+
}) => unknown;
|
|
25
|
+
skeleton?: () => unknown;
|
|
26
|
+
'header-right'?: () => unknown;
|
|
27
|
+
/** Row under the header (active filters, chips…), kept visible in every phase. */
|
|
28
|
+
toolbar?: () => unknown;
|
|
29
|
+
footer?: () => unknown;
|
|
30
|
+
};
|
|
31
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
32
|
+
menu: DashboardMenu;
|
|
33
|
+
card: boolean;
|
|
34
|
+
freshness: boolean;
|
|
35
|
+
isEmpty: boolean;
|
|
36
|
+
skeleton: DashboardSkeletonKind;
|
|
37
|
+
viewAsTable: boolean;
|
|
38
|
+
expandable: boolean;
|
|
39
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
40
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
41
|
+
declare const _default: typeof __VLS_export;
|
|
42
|
+
export default _default;
|
|
43
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
44
|
+
new (): {
|
|
45
|
+
$slots: S;
|
|
46
|
+
};
|
|
47
|
+
};
|