nuxt-ui-tools 1.8.1 → 1.9.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/runtime/dashboard/components/dashboard-stat.vue +4 -2
- package/dist/runtime/dashboard/components/dashboard-stats.vue +11 -2
- package/dist/runtime/dashboard/composables/use-dashboard-tween.d.ts +16 -0
- package/dist/runtime/dashboard/composables/use-dashboard-tween.js +20 -0
- package/dist/runtime/form/composables/use-field-options.js +5 -5
- package/dist/runtime/form/types/options.d.ts +5 -1
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -6,6 +6,7 @@ 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
8
|
import { DASHBOARD_MISSING_VALUE, useDashboardFormat } from "../composables/use-dashboard-format";
|
|
9
|
+
import { countedFigure, tweenTarget, useDashboardTween } from "../composables/use-dashboard-tween";
|
|
9
10
|
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
11
|
import { resolveDashboardColor } from "../utils/charts";
|
|
11
12
|
import { resolveDashboardSparkBars, resolveDashboardSparkline } from "../utils/sparkline";
|
|
@@ -73,11 +74,12 @@ const ready = computed(() => {
|
|
|
73
74
|
});
|
|
74
75
|
const resolved = computed(() => ready.value ? value(ready.value.data) : void 0);
|
|
75
76
|
const formatValue = computed(() => formats.resolve(format));
|
|
77
|
+
const counted = useDashboardTween(() => [tweenTarget(resolved.value)]);
|
|
76
78
|
const display = computed(() => {
|
|
77
79
|
if (!ready.value) return "";
|
|
78
80
|
const shown = resolved.value;
|
|
79
81
|
if (shown === null || shown === void 0) return DASHBOARD_MISSING_VALUE;
|
|
80
|
-
return isNumber(shown) ? formatValue.value(shown) : resolveTextValue(shown);
|
|
82
|
+
return isNumber(shown) ? formatValue.value(countedFigure(shown, counted.value[0])) : resolveTextValue(shown);
|
|
81
83
|
});
|
|
82
84
|
const previous = computed(() => {
|
|
83
85
|
if (!ready.value || !compare) return null;
|
|
@@ -201,7 +203,7 @@ const extra = computed(() => trend ? "trend" : goal ? "goal" : void 0);
|
|
|
201
203
|
</template>
|
|
202
204
|
|
|
203
205
|
<template v-if="ready">
|
|
204
|
-
<div :class="classes.value">
|
|
206
|
+
<div :class="classes.value" data-stat-value>
|
|
205
207
|
<slot name="value" :data="ready.data" :value="display">{{ display }}</slot>
|
|
206
208
|
</div>
|
|
207
209
|
<div v-if="change || captionText || $slots.caption" :class="classes.meta">
|
|
@@ -6,6 +6,7 @@ import { useResponsiveValue } from "#ui-tools/shared/composables/use-responsive-
|
|
|
6
6
|
import { isNumber, isString } from "#ui-tools/shared/utils/predicate";
|
|
7
7
|
import { resolveTextValue } from "#ui-tools/shared/utils/render";
|
|
8
8
|
import { useDashboardFormat } from "../composables/use-dashboard-format";
|
|
9
|
+
import { countedFigure, tweenTarget, useDashboardTween } from "../composables/use-dashboard-tween";
|
|
9
10
|
import { useDashboardUi } from "../composables/use-dashboard-ui";
|
|
10
11
|
import { resolveDashboardColor } from "../utils/charts";
|
|
11
12
|
import { toDashboardCell } from "../utils/export";
|
|
@@ -100,6 +101,14 @@ const entries = computed(() => {
|
|
|
100
101
|
};
|
|
101
102
|
});
|
|
102
103
|
});
|
|
104
|
+
const counted = useDashboardTween(
|
|
105
|
+
() => items.map((_item, index) => tweenTarget(entries.value[index]?.raw))
|
|
106
|
+
);
|
|
107
|
+
const shown = computed(
|
|
108
|
+
() => entries.value.map(
|
|
109
|
+
(entry, index) => isNumber(entry.raw) ? formats.resolve(items[index]?.format)(countedFigure(entry.raw, counted.value[index])) : entry.value
|
|
110
|
+
)
|
|
111
|
+
);
|
|
103
112
|
function tabulate() {
|
|
104
113
|
const hasDelta = items.some((item) => item.delta);
|
|
105
114
|
return {
|
|
@@ -142,7 +151,7 @@ function tabulate() {
|
|
|
142
151
|
</template>
|
|
143
152
|
|
|
144
153
|
<div :class="classes.grid" :style="gridColumns ?? void 0" :data-variant="variant">
|
|
145
|
-
<div v-for="entry in entries" :key="entry.key" :class="classes.item" data-stats-item>
|
|
154
|
+
<div v-for="(entry, index) in entries" :key="entry.key" :class="classes.item" data-stats-item>
|
|
146
155
|
<div class="flex min-w-0 items-center gap-2">
|
|
147
156
|
<span
|
|
148
157
|
v-if="entry.icon"
|
|
@@ -157,7 +166,7 @@ function tabulate() {
|
|
|
157
166
|
<span :class="classes.label">{{ entry.label }}</span>
|
|
158
167
|
</div>
|
|
159
168
|
<div :class="classes.value">
|
|
160
|
-
<span class="truncate">{{
|
|
169
|
+
<span class="truncate">{{ shown[index] }}</span>
|
|
161
170
|
<span
|
|
162
171
|
v-if="entry.status"
|
|
163
172
|
:class="classes.status"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue';
|
|
2
|
+
/** How long a figure takes to count toward a new value. */
|
|
3
|
+
export declare const DASHBOARD_TWEEN_DURATION = 700;
|
|
4
|
+
/**
|
|
5
|
+
* Counts figures toward their new values instead of jumping to them: on the first load, a filter
|
|
6
|
+
* change, or a refresh. Keep the list the same length between updates, since each position counts
|
|
7
|
+
* from its previous value. Figures change at once when the user prefers reduced motion.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useDashboardTween(figures: MaybeRefOrGetter<number[]>): import("vue").ComputedRef<number[]>;
|
|
10
|
+
/**
|
|
11
|
+
* The figure to show while counting toward `target`. Whole numbers count through whole numbers, so
|
|
12
|
+
* a count never reads "12.3" on its way to 21; values that cannot be counted show as they are.
|
|
13
|
+
*/
|
|
14
|
+
export declare function countedFigure(target: number, current: number | undefined): number;
|
|
15
|
+
/** A figure a tween can count toward: finite numbers, and zero for anything else. */
|
|
16
|
+
export declare function tweenTarget(value: unknown): number;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TransitionPresets, usePreferredReducedMotion, useTransition } from "@vueuse/core";
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { isNumber } from "../../shared/utils/predicate.js";
|
|
4
|
+
export const DASHBOARD_TWEEN_DURATION = 700;
|
|
5
|
+
export function useDashboardTween(figures) {
|
|
6
|
+
const reducedMotion = usePreferredReducedMotion();
|
|
7
|
+
return useTransition(figures, {
|
|
8
|
+
disabled: computed(() => reducedMotion.value === "reduce"),
|
|
9
|
+
duration: DASHBOARD_TWEEN_DURATION,
|
|
10
|
+
transition: TransitionPresets.easeOutCubic
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function countedFigure(target, current) {
|
|
14
|
+
if (!Number.isFinite(target)) return target;
|
|
15
|
+
const shown = current !== void 0 && Number.isFinite(current) ? current : target;
|
|
16
|
+
return Number.isInteger(target) ? Math.round(shown) : shown;
|
|
17
|
+
}
|
|
18
|
+
export function tweenTarget(value) {
|
|
19
|
+
return isNumber(value) && Number.isFinite(value) ? value : 0;
|
|
20
|
+
}
|
|
@@ -64,6 +64,9 @@ export function useFieldOptions(params) {
|
|
|
64
64
|
() => contextResources.value.find((resource) => resource.error !== null)?.error ?? null
|
|
65
65
|
);
|
|
66
66
|
const optionConfig = computed(() => resolveOptionConfig(params.field()));
|
|
67
|
+
const declaresOptions = computed(
|
|
68
|
+
() => createFormFieldInstance(params.field()).capability.has("options") && !isUndefined(Object.getOwnPropertyDescriptor(params.field(), "options")?.value)
|
|
69
|
+
);
|
|
67
70
|
const querySource = computed(() => {
|
|
68
71
|
const source = resolvedSource.value;
|
|
69
72
|
return isRuntimeQueryOptions(source) ? source : null;
|
|
@@ -303,13 +306,10 @@ export function useFieldOptions(params) {
|
|
|
303
306
|
params.api.value.value.set([...selected, value]);
|
|
304
307
|
}
|
|
305
308
|
function clearInvalidValue(options) {
|
|
306
|
-
if (
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (pending.value) {
|
|
309
|
+
if (remoteConfig.value || pending.value || error.value !== null) {
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
|
-
if (optionConfig.value?.clearOnInvalid === false) {
|
|
312
|
+
if (!declaresOptions.value || optionConfig.value?.clearOnInvalid === false) {
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
const current = params.api.value.value.get();
|
|
@@ -62,7 +62,11 @@ export interface FormOptionConfig<TOption, TContext = NonNullable<unknown>, TDep
|
|
|
62
62
|
create?: FormCreateOption<TOption, TContext, TDeps, TValue>;
|
|
63
63
|
/** Enables the field-level refresh affordance. Matches shared-ui `allowOptionsRefresh`. */
|
|
64
64
|
allowOptionsRefresh?: boolean;
|
|
65
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Clears current values that no longer exist in resolved options. Defaults to `true`, which
|
|
67
|
+
* shorthand sources (arrays, functions, promises, query options) always use. Values are kept
|
|
68
|
+
* while the source is pending or failed.
|
|
69
|
+
*/
|
|
66
70
|
clearOnInvalid?: boolean;
|
|
67
71
|
/** Runs when resolved source options change, excluding locally-created options. */
|
|
68
72
|
onOptionsChange?: (options: readonly TOption[], params: FormOptionsChangeParams<TContext, TDeps, TValue, TOption>) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-ui-tools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Nuxt module and runtime toolkit for UI-heavy Nuxt applications",
|
|
5
5
|
"homepage": "https://github.com/ChronicStone/nuxt-ui-tools#readme",
|
|
6
6
|
"bugs": "https://github.com/ChronicStone/nuxt-ui-tools/issues",
|