nuxt-ui-tools 1.1.0 → 1.3.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.json +1 -1
- package/dist/module.mjs +10 -1
- package/dist/runtime/dashboard/components/block/dashboard-block-state.d.vue.ts +2 -0
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue +4 -1
- package/dist/runtime/dashboard/components/block/dashboard-block-state.vue.d.ts +2 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.d.vue.ts +20 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.vue +42 -0
- package/dist/runtime/dashboard/components/block/dashboard-chips.vue.d.ts +20 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.d.vue.ts +8 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue +29 -0
- package/dist/runtime/dashboard/components/block/dashboard-series-picker.vue.d.ts +8 -0
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue +6 -3
- package/dist/runtime/dashboard/components/block/dashboard-skeleton.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.d.vue.ts +21 -0
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue +12 -0
- package/dist/runtime/dashboard/components/block/dashboard-view-scope.vue.d.ts +21 -0
- package/dist/runtime/dashboard/components/dashboard-alerts.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-bar-chart.d.vue.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue +30 -9
- package/dist/runtime/dashboard/components/dashboard-bar-chart.vue.d.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-bars.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-card.vue +28 -6
- package/dist/runtime/dashboard/components/dashboard-combo-chart.d.vue.ts +6 -1
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue +13 -3
- package/dist/runtime/dashboard/components/dashboard-combo-chart.vue.d.ts +6 -1
- package/dist/runtime/dashboard/components/dashboard-donut-chart.vue +5 -5
- package/dist/runtime/dashboard/components/dashboard-feed.vue +8 -7
- package/dist/runtime/dashboard/components/dashboard-filter.d.vue.ts +67 -0
- package/dist/runtime/dashboard/components/dashboard-filter.vue +134 -0
- package/dist/runtime/dashboard/components/dashboard-filter.vue.d.ts +67 -0
- package/dist/runtime/dashboard/components/dashboard-filters.d.vue.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-filters.vue +96 -0
- package/dist/runtime/dashboard/components/dashboard-filters.vue.d.ts +49 -0
- package/dist/runtime/dashboard/components/dashboard-funnel.vue +5 -5
- package/dist/runtime/dashboard/components/dashboard-gauge.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-grid.d.vue.ts +10 -3
- package/dist/runtime/dashboard/components/dashboard-grid.vue +24 -9
- package/dist/runtime/dashboard/components/dashboard-grid.vue.d.ts +10 -3
- package/dist/runtime/dashboard/components/dashboard-line-chart.d.vue.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue +30 -9
- package/dist/runtime/dashboard/components/dashboard-line-chart.vue.d.ts +14 -3
- package/dist/runtime/dashboard/components/dashboard-list.d.vue.ts +3 -0
- package/dist/runtime/dashboard/components/dashboard-list.vue +18 -6
- package/dist/runtime/dashboard/components/dashboard-list.vue.d.ts +3 -0
- package/dist/runtime/dashboard/components/dashboard-page.d.vue.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-page.vue +156 -0
- package/dist/runtime/dashboard/components/dashboard-page.vue.d.ts +53 -0
- package/dist/runtime/dashboard/components/dashboard-paired-bars.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-refresh.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-refresh.vue +1 -0
- package/dist/runtime/dashboard/components/dashboard-refresh.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-stack-bar.vue +4 -4
- package/dist/runtime/dashboard/components/dashboard-stat.d.vue.ts +14 -2
- package/dist/runtime/dashboard/components/dashboard-stat.vue +28 -10
- package/dist/runtime/dashboard/components/dashboard-stat.vue.d.ts +14 -2
- package/dist/runtime/dashboard/components/dashboard-stats.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-table.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-table.vue +3 -3
- package/dist/runtime/dashboard/components/dashboard-table.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-tabs.d.vue.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-tabs.vue.d.ts +1 -1
- package/dist/runtime/dashboard/components/dashboard-total.d.vue.ts +7 -2
- package/dist/runtime/dashboard/components/dashboard-total.vue +10 -2
- package/dist/runtime/dashboard/components/dashboard-total.vue.d.ts +7 -2
- package/dist/runtime/dashboard/components/dashboard-view-tabs.d.vue.ts +33 -0
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue +66 -0
- package/dist/runtime/dashboard/components/dashboard-view-tabs.vue.d.ts +33 -0
- package/dist/runtime/dashboard/components/dashboard-widget.vue +1 -1
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.d.vue.ts +44 -0
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue +220 -0
- package/dist/runtime/dashboard/components/filter/dashboard-filter-menu.vue.d.ts +44 -0
- package/dist/runtime/dashboard/composables/use-dashboard-api.d.ts +6 -1
- package/dist/runtime/dashboard/composables/use-dashboard-api.js +29 -6
- package/dist/runtime/dashboard/composables/use-dashboard-block.d.ts +5 -4
- package/dist/runtime/dashboard/composables/use-dashboard-block.js +32 -15
- package/dist/runtime/dashboard/composables/use-dashboard-chart.d.ts +8 -1
- package/dist/runtime/dashboard/composables/use-dashboard-chart.js +16 -4
- package/dist/runtime/dashboard/composables/use-dashboard-context.d.ts +50 -0
- package/dist/runtime/dashboard/composables/use-dashboard-context.js +56 -0
- package/dist/runtime/dashboard/composables/use-dashboard-derived.js +6 -1
- package/dist/runtime/dashboard/composables/use-dashboard-filter-control.d.ts +16 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-control.js +161 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.d.ts +37 -0
- package/dist/runtime/dashboard/composables/use-dashboard-filter-scope.js +107 -0
- package/dist/runtime/dashboard/composables/use-dashboard-format.d.ts +46 -6
- package/dist/runtime/dashboard/composables/use-dashboard-format.js +51 -8
- package/dist/runtime/dashboard/composables/use-dashboard-options.d.ts +11 -4
- package/dist/runtime/dashboard/composables/use-dashboard-options.js +33 -47
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.d.ts +8 -5
- package/dist/runtime/dashboard/composables/use-dashboard-remote-options.js +66 -77
- package/dist/runtime/dashboard/composables/use-dashboard-resource.d.ts +15 -4
- package/dist/runtime/dashboard/composables/use-dashboard-resource.js +52 -17
- package/dist/runtime/dashboard/composables/use-dashboard-scope.d.ts +10 -7
- package/dist/runtime/dashboard/composables/use-dashboard-scope.js +26 -16
- package/dist/runtime/dashboard/composables/use-dashboard-series-picker.d.ts +20 -0
- package/dist/runtime/dashboard/composables/use-dashboard-series-picker.js +30 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.d.ts +2 -0
- package/dist/runtime/dashboard/composables/use-dashboard-time.js +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-ui.d.ts +12 -2
- package/dist/runtime/dashboard/composables/use-dashboard-ui.js +3 -0
- package/dist/runtime/dashboard/composables/use-dashboard-views.d.ts +15 -3
- package/dist/runtime/dashboard/composables/use-dashboard-views.js +33 -10
- package/dist/runtime/dashboard/composables/use-dashboard.d.ts +18 -12
- package/dist/runtime/dashboard/composables/use-dashboard.js +98 -9
- package/dist/runtime/dashboard/index.d.ts +3 -1
- package/dist/runtime/dashboard/index.js +3 -1
- package/dist/runtime/dashboard/schema/index.d.ts +62 -30
- package/dist/runtime/dashboard/schema/index.js +3 -0
- package/dist/runtime/dashboard/types/blocks.d.ts +30 -4
- package/dist/runtime/dashboard/types/charts.d.ts +2 -2
- package/dist/runtime/dashboard/types/controls.d.ts +123 -0
- package/dist/runtime/dashboard/types/filters.d.ts +236 -0
- package/dist/runtime/dashboard/types/index.d.ts +3 -2
- package/dist/runtime/dashboard/types/page.d.ts +17 -0
- package/dist/runtime/dashboard/types/page.js +0 -0
- package/dist/runtime/dashboard/types/resource.d.ts +66 -28
- package/dist/runtime/dashboard/types/runtime.d.ts +39 -8
- package/dist/runtime/dashboard/types/schema.d.ts +94 -54
- package/dist/runtime/dashboard/types/ui.d.ts +78 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-filters.d.ts +142 -0
- package/dist/runtime/dashboard/utils/builders/dashboard-filters.js +135 -0
- package/dist/runtime/dashboard/utils/charts.d.ts +4 -15
- package/dist/runtime/dashboard/utils/charts.js +26 -28
- package/dist/runtime/dashboard/utils/{params/codecs.d.ts → codecs.d.ts} +2 -2
- package/dist/runtime/dashboard/utils/{params/codecs.js → codecs.js} +1 -1
- package/dist/runtime/dashboard/utils/environment.d.ts +36 -0
- package/dist/runtime/dashboard/utils/environment.js +30 -0
- package/dist/runtime/dashboard/utils/filters.d.ts +44 -0
- package/dist/runtime/dashboard/utils/filters.js +62 -0
- package/dist/runtime/dashboard/utils/format.d.ts +25 -0
- package/dist/runtime/dashboard/utils/format.js +66 -0
- package/dist/runtime/dashboard/utils/grid.d.ts +17 -0
- package/dist/runtime/dashboard/utils/grid.js +14 -0
- package/dist/runtime/dashboard/utils/options.d.ts +3 -8
- package/dist/runtime/dashboard/utils/options.js +6 -5
- package/dist/runtime/dashboard/utils/remote.d.ts +7 -0
- package/dist/runtime/dashboard/utils/remote.js +14 -0
- package/dist/runtime/dashboard/utils/resource.d.ts +16 -7
- package/dist/runtime/dashboard/utils/resource.js +15 -9
- package/dist/runtime/dashboard/utils/schema.d.ts +8 -3
- package/dist/runtime/dashboard/utils/schema.js +9 -4
- package/dist/runtime/dashboard/utils/state.d.ts +11 -6
- package/dist/runtime/dashboard/utils/state.js +10 -5
- package/dist/runtime/dashboard/utils/time.d.ts +2 -0
- package/dist/runtime/dashboard/utils/time.js +11 -1
- package/dist/runtime/dashboard/utils/ui.d.ts +1 -1
- package/dist/runtime/dashboard/utils/ui.js +1 -1
- package/dist/runtime/form/components/root/form.vue +1 -1
- package/dist/runtime/form/fields/card-selection.d.ts +1 -0
- package/dist/runtime/form/fields/card-selection.js +1 -0
- package/dist/runtime/form/fields/checkbox-card/component.vue +3 -1
- package/dist/runtime/form/fields/checkbox-group/component.vue +5 -0
- package/dist/runtime/form/fields/radio/component.vue +10 -0
- package/dist/runtime/form/fields/radio-card/component.vue +3 -1
- package/dist/runtime/form/types/options.d.ts +1 -11
- package/dist/runtime/form/utils/focus.js +1 -1
- package/dist/runtime/i18n/locales/en.js +32 -1
- package/dist/runtime/i18n/locales/fr.js +32 -1
- package/dist/runtime/i18n/types.d.ts +44 -0
- package/dist/runtime/shared/styles/scrollbars.css +1 -1
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/types/query.d.ts +8 -0
- package/dist/runtime/shared/utils/remote-options.d.ts +10 -0
- package/dist/runtime/shared/utils/remote-options.js +6 -0
- package/dist/runtime/table/components/filters/shared/filter-option-list-end.d.vue.ts +11 -0
- package/dist/runtime/table/components/filters/shared/filter-option-list-end.vue +76 -0
- package/dist/runtime/table/components/filters/shared/filter-option-list-end.vue.d.ts +11 -0
- package/dist/runtime/table/components/filters/shared/filter-option-loading-list.d.vue.ts +3 -0
- package/dist/runtime/table/components/filters/shared/filter-option-loading-list.vue +2 -1
- package/dist/runtime/table/components/filters/shared/filter-option-loading-list.vue.d.ts +3 -0
- package/dist/runtime/table/components/filters/shared/filter-option-multiple-list.vue +1 -1
- package/dist/runtime/table/components/filters/shared/filter-option-picker-content.vue +13 -0
- package/dist/runtime/table/components/filters/shared/filter-option-single-list.d.vue.ts +2 -0
- package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue +1 -1
- package/dist/runtime/table/components/filters/shared/filter-option-single-list.vue.d.ts +2 -0
- package/dist/runtime/table/components/filters/shared/filter-option-tree-content.d.vue.ts +3 -0
- package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue +2 -2
- package/dist/runtime/table/components/filters/shared/filter-option-tree-content.vue.d.ts +3 -0
- package/dist/runtime/table/components/filters/shared/filter-searchable-panel.vue +1 -0
- package/dist/runtime/table/composables/use-option-filter-editor-state.d.ts +13 -1
- package/dist/runtime/table/composables/use-option-filter-editor-state.js +6 -2
- package/dist/runtime/table/composables/use-table-filter-options.d.ts +11 -1
- package/dist/runtime/table/composables/use-table-filter-options.js +128 -17
- package/dist/runtime/table/composables/use-table-filter-selected-options.d.ts +20 -0
- package/dist/runtime/table/composables/use-table-filter-selected-options.js +99 -0
- package/dist/runtime/table/composables/use-table-filters.d.ts +5 -0
- package/dist/runtime/table/composables/use-table-filters.js +20 -1
- package/dist/runtime/table/composables/use-table-internals.d.ts +15 -0
- package/dist/runtime/table/composables/use-table-remote-filter-options.d.ts +42 -0
- package/dist/runtime/table/composables/use-table-remote-filter-options.js +102 -0
- package/dist/runtime/table/types/filters.d.ts +42 -0
- package/dist/runtime/table/types/index.d.ts +1 -0
- package/dist/runtime/table/types/remote-options.d.ts +64 -0
- package/dist/runtime/table/types/remote-options.js +0 -0
- package/dist/runtime/table/types/source.d.ts +15 -1
- package/dist/runtime/table/utils/builders/table-source.d.ts +2 -6
- package/dist/runtime/table/utils/filters/options.d.ts +2 -0
- package/dist/runtime/table/utils/filters/options.js +1 -1
- package/dist/runtime/table/utils/index.d.ts +1 -0
- package/dist/runtime/table/utils/index.js +1 -0
- package/dist/runtime/table/utils/prefetch.js +21 -1
- package/dist/runtime/table/utils/remote-table-options.d.ts +26 -0
- package/dist/runtime/table/utils/remote-table-options.js +88 -0
- package/package.json +4 -1
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.d.ts +0 -30
- package/dist/runtime/dashboard/composables/use-dashboard-param-scope.js +0 -71
- package/dist/runtime/dashboard/types/options.d.ts +0 -46
- package/dist/runtime/dashboard/types/params.d.ts +0 -115
- package/dist/runtime/dashboard/utils/builders/dashboard-params.d.ts +0 -85
- package/dist/runtime/dashboard/utils/builders/dashboard-params.js +0 -87
- /package/dist/runtime/dashboard/types/{options.js → controls.js} +0 -0
- /package/dist/runtime/dashboard/types/{params.js → filters.js} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
1
2
|
import type { LazyTextValue } from '#ui-tools/shared/types/utils';
|
|
2
3
|
import type { DashboardBlockBaseProps, DashboardBlockUi, DashboardListUi, DashboardRowActions, DashboardSelected, DashboardSelectEvent, DashboardSeriesColor, DashboardSourceLike, DashboardValueFormat } from '../types/index.js';
|
|
3
4
|
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<{
|
|
@@ -27,6 +28,8 @@ declare const __VLS_export: <TRow>(__VLS_props: NonNullable<Awaited<typeof __VLS
|
|
|
27
28
|
rowKey?: (row: TRow, index: number) => PropertyKey;
|
|
28
29
|
/** Rows shown as selected (the value a `select` handler stored). */
|
|
29
30
|
selected?: DashboardSelected<TRow>;
|
|
31
|
+
/** Makes each row a link to what this returns (rows returning `undefined` stay plain). */
|
|
32
|
+
to?: (row: TRow, index: number) => RouteLocationRaw | undefined;
|
|
30
33
|
/** Actions of each row: inline icon buttons and a `⋮` menu at the end of the row. */
|
|
31
34
|
rowActions?: DashboardRowActions<TRow>;
|
|
32
35
|
/** Makes each row a button. */
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ButtonProps } from '@nuxt/ui/components/Button.vue';
|
|
2
|
+
import type { LazyTextValue } from '#ui-tools/shared/types/utils';
|
|
3
|
+
import type { DashboardPageTarget, DashboardPageUi, DashboardViewKeysOf } from '../types/index.js';
|
|
4
|
+
declare const __VLS_export: <TDashboard extends DashboardPageTarget>(__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<{
|
|
5
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
6
|
+
dashboard: TDashboard;
|
|
7
|
+
title?: LazyTextValue;
|
|
8
|
+
/**
|
|
9
|
+
* Line under the title. Defaults to today's date and when the data on screen was fetched;
|
|
10
|
+
* `false` removes it.
|
|
11
|
+
*/
|
|
12
|
+
description?: LazyTextValue | false;
|
|
13
|
+
/** Buttons before the refresh control. A button with an icon shows only the icon on phones. */
|
|
14
|
+
actions?: readonly ButtonProps[];
|
|
15
|
+
/** The refresh control and its auto-refresh menu. Defaults to `true`. */
|
|
16
|
+
refresh?: boolean;
|
|
17
|
+
/** The view tabs, while two views or more are enabled. Defaults to `true`. */
|
|
18
|
+
tabs?: boolean;
|
|
19
|
+
/** The filter bar. Defaults to `true`. */
|
|
20
|
+
filters?: boolean;
|
|
21
|
+
/** Keeps the tabs and the filter bar pinned while the page scrolls. Defaults to `true`. */
|
|
22
|
+
sticky?: boolean;
|
|
23
|
+
ui?: DashboardPageUi;
|
|
24
|
+
}> & (typeof globalThis extends {
|
|
25
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
26
|
+
} ? P : {});
|
|
27
|
+
expose: (exposed: {}) => void;
|
|
28
|
+
attrs: any;
|
|
29
|
+
slots: { [TKey in DashboardViewKeysOf<TDashboard>]?: (() => unknown) | undefined; } & {
|
|
30
|
+
/** Content of a dashboard without views, or of a view without its own slot. */
|
|
31
|
+
default?: (props: {
|
|
32
|
+
view: DashboardViewKeysOf<TDashboard> | undefined;
|
|
33
|
+
}) => unknown;
|
|
34
|
+
title?: () => unknown;
|
|
35
|
+
description?: () => unknown;
|
|
36
|
+
/** Replaces the page actions and the refresh control. */
|
|
37
|
+
actions?: () => unknown;
|
|
38
|
+
/** Replaces the view tabs. */
|
|
39
|
+
tabs?: () => unknown;
|
|
40
|
+
/** Replaces the filter bar, e.g. with a `UiDashboardFilters` that customizes some filters. */
|
|
41
|
+
filters?: () => unknown;
|
|
42
|
+
};
|
|
43
|
+
emit: {};
|
|
44
|
+
}>) => import("vue").VNode & {
|
|
45
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
46
|
+
};
|
|
47
|
+
declare const _default: typeof __VLS_export;
|
|
48
|
+
export default _default;
|
|
49
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
50
|
+
[K in keyof T]: T[K];
|
|
51
|
+
} : {
|
|
52
|
+
[K in keyof T as K]: T[K];
|
|
53
|
+
}) & {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UButton from "@nuxt/ui/components/Button.vue";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
import { computed, onBeforeUnmount, shallowRef, useTemplateRef, watch } from "vue";
|
|
5
|
+
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
6
|
+
import { isObject, isString } from "#ui-tools/shared/utils/predicate";
|
|
7
|
+
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
8
|
+
import { useDashboardTime } from "../composables/use-dashboard-time";
|
|
9
|
+
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
|
+
import { resolveDashboardClasses } from "../utils/ui";
|
|
11
|
+
import DashboardViewScope from "./block/dashboard-view-scope.vue";
|
|
12
|
+
import DashboardFilters from "./dashboard-filters.vue";
|
|
13
|
+
import DashboardRefresh from "./dashboard-refresh.vue";
|
|
14
|
+
import DashboardRelativeTime from "./dashboard-relative-time.vue";
|
|
15
|
+
import DashboardViewTabs from "./dashboard-view-tabs.vue";
|
|
16
|
+
const {
|
|
17
|
+
dashboard,
|
|
18
|
+
title = void 0,
|
|
19
|
+
description = void 0,
|
|
20
|
+
actions = [],
|
|
21
|
+
refresh = true,
|
|
22
|
+
tabs = true,
|
|
23
|
+
filters = true,
|
|
24
|
+
sticky = true,
|
|
25
|
+
ui
|
|
26
|
+
} = defineProps({
|
|
27
|
+
dashboard: { type: null, required: true },
|
|
28
|
+
title: { type: [String, Number, Function], required: false },
|
|
29
|
+
description: { type: [String, Number, Function, Boolean], required: false },
|
|
30
|
+
actions: { type: Array, required: false },
|
|
31
|
+
refresh: { type: Boolean, required: false },
|
|
32
|
+
tabs: { type: Boolean, required: false },
|
|
33
|
+
filters: { type: Boolean, required: false },
|
|
34
|
+
sticky: { type: Boolean, required: false },
|
|
35
|
+
ui: { type: Object, required: false }
|
|
36
|
+
});
|
|
37
|
+
defineSlots();
|
|
38
|
+
const { t } = useUiToolsLocale();
|
|
39
|
+
const time = useDashboardTime();
|
|
40
|
+
const appUi = useDashboardUi();
|
|
41
|
+
const classes = computed(
|
|
42
|
+
() => resolveDashboardClasses(
|
|
43
|
+
{
|
|
44
|
+
actions: "flex min-h-[34px] items-center gap-2",
|
|
45
|
+
body: "flex flex-col gap-4 px-4 pt-1 pb-12 lg:gap-6 lg:px-7 lg:pb-15",
|
|
46
|
+
description: "col-span-full mt-1 text-[12.5px] font-light text-muted md:text-[13.5px]",
|
|
47
|
+
filters: "px-4 py-3 lg:px-7 lg:py-4",
|
|
48
|
+
header: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-x-4 bg-default px-4 pt-4 pb-3.5 lg:px-7 lg:pt-[22px] lg:pb-[18px]",
|
|
49
|
+
root: "h-full overflow-auto bg-muted",
|
|
50
|
+
tabs: "bg-default px-2 lg:px-7",
|
|
51
|
+
title: "m-0 min-w-0 truncate text-2xl leading-[1.1] font-light tracking-[-0.04em] text-highlighted md:text-[30px]",
|
|
52
|
+
toolbar: [
|
|
53
|
+
"z-20 bg-muted transition-shadow duration-200 data-[stuck]:shadow-[0_10px_24px_-18px_rgb(0_0_0/0.35)]",
|
|
54
|
+
sticky && "sticky top-0"
|
|
55
|
+
].filter(Boolean).join(" ")
|
|
56
|
+
},
|
|
57
|
+
appUi.value.page,
|
|
58
|
+
ui
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
const views = computed(() => hasViews(dashboard) ? dashboard : void 0);
|
|
62
|
+
const current = computed(() => views.value?.view.current);
|
|
63
|
+
const marker = useTemplateRef("marker");
|
|
64
|
+
const stuck = shallowRef(false);
|
|
65
|
+
let observer;
|
|
66
|
+
watch(
|
|
67
|
+
() => sticky ? marker.value : null,
|
|
68
|
+
(element) => {
|
|
69
|
+
observer?.disconnect();
|
|
70
|
+
observer = void 0;
|
|
71
|
+
stuck.value = false;
|
|
72
|
+
if (!element || typeof IntersectionObserver === "undefined") return;
|
|
73
|
+
observer = new IntersectionObserver(
|
|
74
|
+
([entry]) => {
|
|
75
|
+
stuck.value = Boolean(
|
|
76
|
+
entry && !entry.isIntersecting && entry.boundingClientRect.top < (entry.rootBounds?.top ?? 0)
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
{ root: scrollContainer(element) }
|
|
80
|
+
);
|
|
81
|
+
observer.observe(element);
|
|
82
|
+
},
|
|
83
|
+
{ flush: "post", immediate: true }
|
|
84
|
+
);
|
|
85
|
+
onBeforeUnmount(() => observer?.disconnect());
|
|
86
|
+
function hasViews(target) {
|
|
87
|
+
return isObject(target.view) && "items" in target.view;
|
|
88
|
+
}
|
|
89
|
+
function scrollContainer(element) {
|
|
90
|
+
for (let node = element.parentElement; node; node = node.parentElement) {
|
|
91
|
+
if (/auto|scroll/u.test(getComputedStyle(node).overflowY)) return node;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function actionUi(action) {
|
|
96
|
+
const label = action.ui?.label;
|
|
97
|
+
if (!action.icon || label !== void 0 && !isString(label)) return action.ui;
|
|
98
|
+
return { ...action.ui, label: twMerge(label, "max-sm:sr-only") };
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<!-- Without a dashboard (its schema threw during setup), render nothing: that error stays the one reported. -->
|
|
104
|
+
<div v-if="dashboard" :class="classes.root" data-dashboard-page>
|
|
105
|
+
<header :class="classes.header">
|
|
106
|
+
<h1 :class="classes.title">
|
|
107
|
+
<slot name="title">{{ title === void 0 ? "" : resolveTextValue(title) }}</slot>
|
|
108
|
+
</h1>
|
|
109
|
+
<div :class="classes.actions">
|
|
110
|
+
<slot name="actions">
|
|
111
|
+
<UButton
|
|
112
|
+
v-for="(action, index) in actions"
|
|
113
|
+
:key="index"
|
|
114
|
+
color="neutral"
|
|
115
|
+
variant="outline"
|
|
116
|
+
size="md"
|
|
117
|
+
v-bind="action"
|
|
118
|
+
:ui="actionUi(action)"
|
|
119
|
+
/>
|
|
120
|
+
<DashboardRefresh v-if="refresh" :dashboard :updated="false" size="md" />
|
|
121
|
+
</slot>
|
|
122
|
+
</div>
|
|
123
|
+
<p v-if="description !== false" :class="classes.description" data-allow-mismatch="text">
|
|
124
|
+
<slot name="description">
|
|
125
|
+
<template v-if="description !== void 0">{{ resolveTextValue(description) }}</template>
|
|
126
|
+
<template v-else>
|
|
127
|
+
{{ time.today() }} ·
|
|
128
|
+
<template v-if="dashboard.updatedAt !== void 0">
|
|
129
|
+
{{ t("dashboard.page.updated") }}
|
|
130
|
+
<DashboardRelativeTime :value="dashboard.updatedAt" />
|
|
131
|
+
</template>
|
|
132
|
+
<template v-else>{{ t("dashboard.page.loading") }}</template>
|
|
133
|
+
</template>
|
|
134
|
+
</slot>
|
|
135
|
+
</p>
|
|
136
|
+
</header>
|
|
137
|
+
|
|
138
|
+
<div ref="marker" aria-hidden="true" />
|
|
139
|
+
<div :class="classes.toolbar" :data-stuck="stuck || void 0">
|
|
140
|
+
<slot name="tabs">
|
|
141
|
+
<DashboardViewTabs v-if="tabs && views" :dashboard="views" :class="classes.tabs" />
|
|
142
|
+
</slot>
|
|
143
|
+
<slot name="filters">
|
|
144
|
+
<DashboardFilters v-if="filters" :dashboard :class="classes.filters" />
|
|
145
|
+
</slot>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div :class="classes.body">
|
|
149
|
+
<DashboardViewScope v-if="current" :key="current" :dashboard :view="current">
|
|
150
|
+
<slot v-if="$slots[current]" :name="current" />
|
|
151
|
+
<slot v-else :view="current" />
|
|
152
|
+
</DashboardViewScope>
|
|
153
|
+
<slot v-else :view="current" />
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ButtonProps } from '@nuxt/ui/components/Button.vue';
|
|
2
|
+
import type { LazyTextValue } from '#ui-tools/shared/types/utils';
|
|
3
|
+
import type { DashboardPageTarget, DashboardPageUi, DashboardViewKeysOf } from '../types/index.js';
|
|
4
|
+
declare const __VLS_export: <TDashboard extends DashboardPageTarget>(__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<{
|
|
5
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<{
|
|
6
|
+
dashboard: TDashboard;
|
|
7
|
+
title?: LazyTextValue;
|
|
8
|
+
/**
|
|
9
|
+
* Line under the title. Defaults to today's date and when the data on screen was fetched;
|
|
10
|
+
* `false` removes it.
|
|
11
|
+
*/
|
|
12
|
+
description?: LazyTextValue | false;
|
|
13
|
+
/** Buttons before the refresh control. A button with an icon shows only the icon on phones. */
|
|
14
|
+
actions?: readonly ButtonProps[];
|
|
15
|
+
/** The refresh control and its auto-refresh menu. Defaults to `true`. */
|
|
16
|
+
refresh?: boolean;
|
|
17
|
+
/** The view tabs, while two views or more are enabled. Defaults to `true`. */
|
|
18
|
+
tabs?: boolean;
|
|
19
|
+
/** The filter bar. Defaults to `true`. */
|
|
20
|
+
filters?: boolean;
|
|
21
|
+
/** Keeps the tabs and the filter bar pinned while the page scrolls. Defaults to `true`. */
|
|
22
|
+
sticky?: boolean;
|
|
23
|
+
ui?: DashboardPageUi;
|
|
24
|
+
}> & (typeof globalThis extends {
|
|
25
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
26
|
+
} ? P : {});
|
|
27
|
+
expose: (exposed: {}) => void;
|
|
28
|
+
attrs: any;
|
|
29
|
+
slots: { [TKey in DashboardViewKeysOf<TDashboard>]?: (() => unknown) | undefined; } & {
|
|
30
|
+
/** Content of a dashboard without views, or of a view without its own slot. */
|
|
31
|
+
default?: (props: {
|
|
32
|
+
view: DashboardViewKeysOf<TDashboard> | undefined;
|
|
33
|
+
}) => unknown;
|
|
34
|
+
title?: () => unknown;
|
|
35
|
+
description?: () => unknown;
|
|
36
|
+
/** Replaces the page actions and the refresh control. */
|
|
37
|
+
actions?: () => unknown;
|
|
38
|
+
/** Replaces the view tabs. */
|
|
39
|
+
tabs?: () => unknown;
|
|
40
|
+
/** Replaces the filter bar, e.g. with a `UiDashboardFilters` that customizes some filters. */
|
|
41
|
+
filters?: () => unknown;
|
|
42
|
+
};
|
|
43
|
+
emit: {};
|
|
44
|
+
}>) => import("vue").VNode & {
|
|
45
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
46
|
+
};
|
|
47
|
+
declare const _default: typeof __VLS_export;
|
|
48
|
+
export default _default;
|
|
49
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
50
|
+
[K in keyof T]: T[K];
|
|
51
|
+
} : {
|
|
52
|
+
[K in keyof T as K]: T[K];
|
|
53
|
+
}) & {};
|
|
@@ -34,12 +34,12 @@ const {
|
|
|
34
34
|
title: { type: [String, Number, Function], required: false },
|
|
35
35
|
subtitle: { type: [String, Number, Function], required: false },
|
|
36
36
|
size: { type: String, required: false },
|
|
37
|
-
rows: { type: String, required: false },
|
|
38
37
|
card: { type: Boolean, required: false },
|
|
39
38
|
activation: { type: String, required: false },
|
|
40
39
|
empty: { type: Object, required: false },
|
|
41
40
|
menu: { type: [Boolean, Array, Function], required: false },
|
|
42
41
|
actions: { type: Array, required: false },
|
|
42
|
+
filters: { type: Array, required: false },
|
|
43
43
|
freshness: { type: Boolean, required: false },
|
|
44
44
|
source: { type: Object, required: true },
|
|
45
45
|
label: { type: Function, required: true },
|
|
@@ -47,7 +47,7 @@ const {
|
|
|
47
47
|
value: { type: Function, required: true },
|
|
48
48
|
totalLabel: { type: [String, Number, Function], required: false },
|
|
49
49
|
valueLabel: { type: [String, Number, Function], required: false },
|
|
50
|
-
format: { type: Function, required: false },
|
|
50
|
+
format: { type: [String, Function], required: false, skipCheck: true },
|
|
51
51
|
colors: { type: Array, required: false },
|
|
52
52
|
limit: { type: Number, required: false },
|
|
53
53
|
rowKey: { type: Function, required: false },
|
|
@@ -78,7 +78,7 @@ const legend = computed(
|
|
|
78
78
|
{ color: valueColor.value, key: "value", label: resolveTextValue(valueLabel) }
|
|
79
79
|
] : void 0
|
|
80
80
|
);
|
|
81
|
-
const formatValue = computed(() =>
|
|
81
|
+
const formatValue = computed(() => formats.resolve(format));
|
|
82
82
|
const entries = computed(
|
|
83
83
|
() => (source.data ?? []).map((row, index) => {
|
|
84
84
|
const outer = total(row);
|
|
@@ -90,7 +90,7 @@ const entries = computed(
|
|
|
90
90
|
key: rowKey?.(row, index) ?? index,
|
|
91
91
|
label: resolveTextValue(label(row)),
|
|
92
92
|
outer,
|
|
93
|
-
ratio: outer > 0 ? formats.percent
|
|
93
|
+
ratio: outer > 0 ? formats.percent(Math.round(inner / outer * 100)) : "",
|
|
94
94
|
row
|
|
95
95
|
};
|
|
96
96
|
})
|
|
@@ -20,7 +20,7 @@ type __VLS_Props = {
|
|
|
20
20
|
intervals?: readonly number[];
|
|
21
21
|
/** Shows when the data on screen was last fetched. */
|
|
22
22
|
updated?: boolean;
|
|
23
|
-
/** Shows the "Refresh" label next to the icon. */
|
|
23
|
+
/** Shows the "Refresh" label next to the icon, from the `sm` breakpoint up (phones get the icon). */
|
|
24
24
|
label?: boolean;
|
|
25
25
|
size?: 'xs' | 'sm' | 'md';
|
|
26
26
|
};
|
|
@@ -64,6 +64,7 @@ function refresh() {
|
|
|
64
64
|
icon="i-lucide-refresh-cw"
|
|
65
65
|
:label="label ? t('dashboard.refresh.label') : void 0"
|
|
66
66
|
:aria-label="label ? void 0 : t('dashboard.refresh.label')"
|
|
67
|
+
:ui="{ label: 'max-sm:sr-only' }"
|
|
67
68
|
:loading="dashboard.refreshing"
|
|
68
69
|
@click="refresh"
|
|
69
70
|
/>
|
|
@@ -20,7 +20,7 @@ type __VLS_Props = {
|
|
|
20
20
|
intervals?: readonly number[];
|
|
21
21
|
/** Shows when the data on screen was last fetched. */
|
|
22
22
|
updated?: boolean;
|
|
23
|
-
/** Shows the "Refresh" label next to the icon. */
|
|
23
|
+
/** Shows the "Refresh" label next to the icon, from the `sm` breakpoint up (phones get the icon). */
|
|
24
24
|
label?: boolean;
|
|
25
25
|
size?: 'xs' | 'sm' | 'md';
|
|
26
26
|
};
|
|
@@ -30,12 +30,12 @@ const {
|
|
|
30
30
|
title: { type: [String, Number, Function], required: false },
|
|
31
31
|
subtitle: { type: [String, Number, Function], required: false },
|
|
32
32
|
size: { type: String, required: false },
|
|
33
|
-
rows: { type: String, required: false },
|
|
34
33
|
card: { type: Boolean, required: false },
|
|
35
34
|
activation: { type: String, required: false },
|
|
36
35
|
empty: { type: Object, required: false },
|
|
37
36
|
menu: { type: [Boolean, Array, Function], required: false },
|
|
38
37
|
actions: { type: Array, required: false },
|
|
38
|
+
filters: { type: Array, required: false },
|
|
39
39
|
freshness: { type: Boolean, required: false },
|
|
40
40
|
source: { type: Object, required: true },
|
|
41
41
|
label: { type: Function, required: true },
|
|
@@ -78,7 +78,7 @@ const parts = computed(() => {
|
|
|
78
78
|
label: resolveTextValue(label(row)),
|
|
79
79
|
row,
|
|
80
80
|
share,
|
|
81
|
-
text: text ? resolveTextValue(text(row, share)) : formats.percent
|
|
81
|
+
text: text ? resolveTextValue(text(row, share)) : formats.percent(share),
|
|
82
82
|
width: `${share}%`
|
|
83
83
|
};
|
|
84
84
|
});
|
|
@@ -92,8 +92,8 @@ function tabulate() {
|
|
|
92
92
|
],
|
|
93
93
|
rows: parts.value.map((part) => [
|
|
94
94
|
toDashboardCell(part.label),
|
|
95
|
-
toDashboardCell(part.amount, formats.number
|
|
96
|
-
toDashboardCell(Math.round(part.share * 10) / 10, formats.percent
|
|
95
|
+
toDashboardCell(part.amount, formats.number(part.amount)),
|
|
96
|
+
toDashboardCell(Math.round(part.share * 10) / 10, formats.percent(part.share))
|
|
97
97
|
])
|
|
98
98
|
};
|
|
99
99
|
}
|
|
@@ -5,14 +5,21 @@ declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VL
|
|
|
5
5
|
source: DashboardSourceLike<TData>;
|
|
6
6
|
/** KPI label, shown above the value. */
|
|
7
7
|
label: LazyTextValue;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Main value. Numbers go through `format` (locale number format by default); `null` /
|
|
10
|
+
* `undefined` (an average without units, a rate without a base) show "—".
|
|
11
|
+
*/
|
|
12
|
+
value: (data: TData & ({} | null)) => LazyTextValue | null | undefined;
|
|
10
13
|
format?: DashboardValueFormat;
|
|
11
14
|
/**
|
|
12
15
|
* Change indicator, in percent by default. `null` hides it. Defaults to the change from
|
|
13
16
|
* `compare` when that is set.
|
|
14
17
|
*/
|
|
15
18
|
delta?: (data: TData & ({} | null)) => number | null | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Format of the delta. Defaults to a signed percent (`delta`), or with `compare-mode="difference"`
|
|
21
|
+
* to `points` for `percent` / `ratio` values and a signed number otherwise.
|
|
22
|
+
*/
|
|
16
23
|
deltaFormat?: DashboardValueFormat;
|
|
17
24
|
/** A decrease is good news (e.g. a failure rate): colors the delta accordingly. */
|
|
18
25
|
invertDelta?: boolean;
|
|
@@ -29,6 +36,11 @@ declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VL
|
|
|
29
36
|
* default `delta`, and the default caption ("vs 1,204 previous period").
|
|
30
37
|
*/
|
|
31
38
|
compare?: (data: TData & ({} | null)) => number | null | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* How the default delta compares `value` with `compare`: the relative change in percent
|
|
41
|
+
* (`change`, default), or the difference (`difference`: `+12` accounts, `+2.1 pts` of margin).
|
|
42
|
+
*/
|
|
43
|
+
compareMode?: "change" | "difference";
|
|
32
44
|
/**
|
|
33
45
|
* Caption shown with `compare`, as text or built from the formatted previous value. Defaults to
|
|
34
46
|
* "vs <value> previous period".
|
|
@@ -5,7 +5,7 @@ import { computed } from "vue";
|
|
|
5
5
|
import { useUiToolsLocale } from "#ui-tools/i18n";
|
|
6
6
|
import { isNumber, isString } from "#ui-tools/shared/utils/predicate";
|
|
7
7
|
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
8
|
-
import { useDashboardFormat } from "../composables/use-dashboard-format";
|
|
8
|
+
import { DASHBOARD_MISSING_VALUE, useDashboardFormat } from "../composables/use-dashboard-format";
|
|
9
9
|
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
10
|
import { resolveDashboardColor } from "../utils/charts";
|
|
11
11
|
import { resolveDashboardSparkBars, resolveDashboardSparkline } from "../utils/sparkline";
|
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
trendType = "area",
|
|
30
30
|
trendColor = "series-1",
|
|
31
31
|
compare,
|
|
32
|
+
compareMode = "change",
|
|
32
33
|
compareLabel,
|
|
33
34
|
goal,
|
|
34
35
|
status,
|
|
@@ -36,19 +37,19 @@ const {
|
|
|
36
37
|
...block
|
|
37
38
|
} = defineProps({
|
|
38
39
|
size: { type: String, required: false },
|
|
39
|
-
rows: { type: String, required: false },
|
|
40
40
|
card: { type: Boolean, required: false },
|
|
41
41
|
activation: { type: String, required: false },
|
|
42
42
|
empty: { type: Object, required: false },
|
|
43
43
|
menu: { type: [Boolean, Array, Function], required: false },
|
|
44
44
|
actions: { type: Array, required: false },
|
|
45
|
+
filters: { type: Array, required: false },
|
|
45
46
|
freshness: { type: Boolean, required: false },
|
|
46
47
|
source: { type: Object, required: true },
|
|
47
48
|
label: { type: [String, Number, Function], required: true },
|
|
48
49
|
value: { type: Function, required: true },
|
|
49
|
-
format: { type: Function, required: false },
|
|
50
|
+
format: { type: [String, Function], required: false, skipCheck: true },
|
|
50
51
|
delta: { type: Function, required: false },
|
|
51
|
-
deltaFormat: { type: Function, required: false },
|
|
52
|
+
deltaFormat: { type: [String, Function], required: false, skipCheck: true },
|
|
52
53
|
invertDelta: { type: Boolean, required: false },
|
|
53
54
|
caption: { type: [String, Function], required: false },
|
|
54
55
|
icon: { type: String, required: false },
|
|
@@ -56,6 +57,7 @@ const {
|
|
|
56
57
|
trendType: { type: String, required: false },
|
|
57
58
|
trendColor: { type: String, required: false },
|
|
58
59
|
compare: { type: Function, required: false },
|
|
60
|
+
compareMode: { type: String, required: false },
|
|
59
61
|
compareLabel: { type: [String, Number, Function], required: false },
|
|
60
62
|
goal: { type: Function, required: false },
|
|
61
63
|
status: { type: Function, required: false },
|
|
@@ -70,22 +72,38 @@ const ready = computed(() => {
|
|
|
70
72
|
return data === void 0 ? null : { data };
|
|
71
73
|
});
|
|
72
74
|
const resolved = computed(() => ready.value ? value(ready.value.data) : void 0);
|
|
73
|
-
const formatValue = computed(() =>
|
|
75
|
+
const formatValue = computed(() => formats.resolve(format));
|
|
74
76
|
const display = computed(() => {
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
+
if (!ready.value) return "";
|
|
78
|
+
const shown = resolved.value;
|
|
79
|
+
if (shown === null || shown === void 0) return DASHBOARD_MISSING_VALUE;
|
|
80
|
+
return isNumber(shown) ? formatValue.value(shown) : resolveTextValue(shown);
|
|
77
81
|
});
|
|
78
82
|
const previous = computed(() => {
|
|
79
83
|
if (!ready.value || !compare) return null;
|
|
80
84
|
const amount = compare(ready.value.data);
|
|
81
85
|
return isNumber(amount) && Number.isFinite(amount) ? amount : null;
|
|
82
86
|
});
|
|
87
|
+
const scale = computed(() => compareMode === "difference" && format === "ratio" ? 100 : 1);
|
|
88
|
+
const deltaFallback = computed(() => {
|
|
89
|
+
if (compareMode !== "difference") return "delta";
|
|
90
|
+
return format === "percent" || format === "ratio" ? "points" : "signed";
|
|
91
|
+
});
|
|
83
92
|
const change = computed(() => {
|
|
84
93
|
if (!ready.value) return null;
|
|
85
|
-
const
|
|
94
|
+
const current = resolved.value;
|
|
95
|
+
const before = previous.value;
|
|
96
|
+
let amount = null;
|
|
97
|
+
if (delta) amount = delta(ready.value.data);
|
|
98
|
+
else if (before !== null && isNumber(current))
|
|
99
|
+
amount = compareMode === "difference" ? (current - before) * scale.value : before === 0 ? null : (current - before) / Math.abs(before) * 100;
|
|
86
100
|
if (amount === null || amount === void 0 || !Number.isFinite(amount)) return null;
|
|
87
101
|
const good = invertDelta ? amount <= 0 : amount >= 0;
|
|
88
|
-
return {
|
|
102
|
+
return {
|
|
103
|
+
good,
|
|
104
|
+
label: formats.resolve(deltaFormat, deltaFallback.value)(amount),
|
|
105
|
+
up: amount >= 0
|
|
106
|
+
};
|
|
89
107
|
});
|
|
90
108
|
const captionText = computed(() => {
|
|
91
109
|
if (!ready.value) return "";
|
|
@@ -113,7 +131,7 @@ const progress = computed(() => {
|
|
|
113
131
|
const completion = resolved.value / target * 100;
|
|
114
132
|
const filled = Math.round(Math.max(0, Math.min(100, completion)));
|
|
115
133
|
return {
|
|
116
|
-
completion: formats.percent
|
|
134
|
+
completion: formats.percent(completion),
|
|
117
135
|
filled,
|
|
118
136
|
goal: t("dashboard.stat.goal", { value: formatValue.value(target) }),
|
|
119
137
|
reached: completion >= 100
|
|
@@ -5,14 +5,21 @@ declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VL
|
|
|
5
5
|
source: DashboardSourceLike<TData>;
|
|
6
6
|
/** KPI label, shown above the value. */
|
|
7
7
|
label: LazyTextValue;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Main value. Numbers go through `format` (locale number format by default); `null` /
|
|
10
|
+
* `undefined` (an average without units, a rate without a base) show "—".
|
|
11
|
+
*/
|
|
12
|
+
value: (data: TData & ({} | null)) => LazyTextValue | null | undefined;
|
|
10
13
|
format?: DashboardValueFormat;
|
|
11
14
|
/**
|
|
12
15
|
* Change indicator, in percent by default. `null` hides it. Defaults to the change from
|
|
13
16
|
* `compare` when that is set.
|
|
14
17
|
*/
|
|
15
18
|
delta?: (data: TData & ({} | null)) => number | null | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Format of the delta. Defaults to a signed percent (`delta`), or with `compare-mode="difference"`
|
|
21
|
+
* to `points` for `percent` / `ratio` values and a signed number otherwise.
|
|
22
|
+
*/
|
|
16
23
|
deltaFormat?: DashboardValueFormat;
|
|
17
24
|
/** A decrease is good news (e.g. a failure rate): colors the delta accordingly. */
|
|
18
25
|
invertDelta?: boolean;
|
|
@@ -29,6 +36,11 @@ declare const __VLS_export: <TData>(__VLS_props: NonNullable<Awaited<typeof __VL
|
|
|
29
36
|
* default `delta`, and the default caption ("vs 1,204 previous period").
|
|
30
37
|
*/
|
|
31
38
|
compare?: (data: TData & ({} | null)) => number | null | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* How the default delta compares `value` with `compare`: the relative change in percent
|
|
41
|
+
* (`change`, default), or the difference (`difference`: `+12` accounts, `+2.1 pts` of margin).
|
|
42
|
+
*/
|
|
43
|
+
compareMode?: "change" | "difference";
|
|
32
44
|
/**
|
|
33
45
|
* Caption shown with `compare`, as text or built from the formatted previous value. Defaults to
|
|
34
46
|
* "vs <value> previous period".
|
|
@@ -26,12 +26,12 @@ const {
|
|
|
26
26
|
title: { type: [String, Number, Function], required: false },
|
|
27
27
|
subtitle: { type: [String, Number, Function], required: false },
|
|
28
28
|
size: { type: String, required: false },
|
|
29
|
-
rows: { type: String, required: false },
|
|
30
29
|
card: { type: Boolean, required: false },
|
|
31
30
|
activation: { type: String, required: false },
|
|
32
31
|
empty: { type: Object, required: false },
|
|
33
32
|
menu: { type: [Boolean, Array, Function], required: false },
|
|
34
33
|
actions: { type: Array, required: false },
|
|
34
|
+
filters: { type: Array, required: false },
|
|
35
35
|
freshness: { type: Boolean, required: false },
|
|
36
36
|
source: { type: Object, required: true },
|
|
37
37
|
items: { type: Array, required: true },
|
|
@@ -89,14 +89,14 @@ const entries = computed(() => {
|
|
|
89
89
|
caption: resolveTextValue(caption),
|
|
90
90
|
change: isNumber(amount) && Number.isFinite(amount) ? amount : null,
|
|
91
91
|
color: resolveDashboardColor(item.color, index),
|
|
92
|
-
delta: isNumber(amount) && Number.isFinite(amount) ? { good, label: formats.delta
|
|
92
|
+
delta: isNumber(amount) && Number.isFinite(amount) ? { good, label: formats.delta(amount), up: amount >= 0 } : null,
|
|
93
93
|
icon: item.icon,
|
|
94
94
|
key: item.key,
|
|
95
95
|
label: resolveTextValue(item.label),
|
|
96
96
|
progress: isNumber(done) ? Math.round(Math.max(0, Math.min(100, done))) : null,
|
|
97
97
|
raw,
|
|
98
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
|
|
99
|
+
value: raw === null || raw === void 0 ? "\u2013" : isNumber(raw) ? formats.resolve(item.format)(raw) : resolveTextValue(raw)
|
|
100
100
|
};
|
|
101
101
|
});
|
|
102
102
|
});
|
|
@@ -18,7 +18,7 @@ declare const __VLS_export: <TRow>(__VLS_props: NonNullable<Awaited<typeof __VLS
|
|
|
18
18
|
} & {
|
|
19
19
|
/**
|
|
20
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
|
|
21
|
+
* ascending), the opposite one, then source order. Bind it to keep the sort in a filter.
|
|
22
22
|
*/
|
|
23
23
|
sort?: DashboardTableSort | null | undefined;
|
|
24
24
|
}) & {
|
|
@@ -34,12 +34,12 @@ const {
|
|
|
34
34
|
title: { type: [String, Number, Function], required: false },
|
|
35
35
|
subtitle: { type: [String, Number, Function], required: false },
|
|
36
36
|
size: { type: String, required: false },
|
|
37
|
-
rows: { type: String, required: false },
|
|
38
37
|
card: { type: Boolean, required: false },
|
|
39
38
|
activation: { type: String, required: false },
|
|
40
39
|
empty: { type: Object, required: false },
|
|
41
40
|
menu: { type: [Boolean, Array, Function], required: false },
|
|
42
41
|
actions: { type: Array, required: false },
|
|
42
|
+
filters: { type: Array, required: false },
|
|
43
43
|
freshness: { type: Boolean, required: false },
|
|
44
44
|
source: { type: Object, required: true },
|
|
45
45
|
columns: { type: Array, required: true },
|
|
@@ -93,9 +93,9 @@ const entries = computed(() => {
|
|
|
93
93
|
const { column, type } = resolved;
|
|
94
94
|
if (type === "delta") {
|
|
95
95
|
const good = column.invert ? value <= 0 : value >= 0;
|
|
96
|
-
return { good, text: (column.format
|
|
96
|
+
return { good, text: formats.resolve(column.format, "delta")(value), value };
|
|
97
97
|
}
|
|
98
|
-
const format = column.format
|
|
98
|
+
const format = formats.resolve(column.format, type === "percent" ? "percent" : "number");
|
|
99
99
|
const max = maxima[position] ?? 0;
|
|
100
100
|
const width = type === "bar" && max > 0 ? `${Math.max(0, Math.min(100, value / max * 100))}%` : void 0;
|
|
101
101
|
return { text: format(value), value, width };
|
|
@@ -18,7 +18,7 @@ declare const __VLS_export: <TRow>(__VLS_props: NonNullable<Awaited<typeof __VLS
|
|
|
18
18
|
} & {
|
|
19
19
|
/**
|
|
20
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
|
|
21
|
+
* ascending), the opposite one, then source order. Bind it to keep the sort in a filter.
|
|
22
22
|
*/
|
|
23
23
|
sort?: DashboardTableSort | null | undefined;
|
|
24
24
|
}) & {
|
|
@@ -9,7 +9,7 @@ declare const __VLS_export: <TValue extends string | number>(__VLS_props: NonNul
|
|
|
9
9
|
full?: boolean;
|
|
10
10
|
} & {
|
|
11
11
|
/**
|
|
12
|
-
* Tab strip for dashboard cards, bound with `v-model`. Bind it to a
|
|
12
|
+
* Tab strip for dashboard cards, bound with `v-model`. Bind it to a query filter to switch what a
|
|
13
13
|
* block fetches (the tab lands in the URL, and the block refreshes in place), or to a local ref to
|
|
14
14
|
* switch between blocks: render only the active one, so the others' deferred queries stay idle.
|
|
15
15
|
*/
|