nuxt-ui-tools 1.23.0 → 1.25.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 +8 -0
- package/dist/runtime/display/components/blocks/display-accordion.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-accordion.vue +93 -0
- package/dist/runtime/display/components/blocks/display-accordion.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-alert.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-alert.vue +133 -0
- package/dist/runtime/display/components/blocks/display-alert.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-breakdown.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-breakdown.vue +178 -0
- package/dist/runtime/display/components/blocks/display-breakdown.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-chart.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-chart.vue +237 -0
- package/dist/runtime/display/components/blocks/display-chart.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-code.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-code.vue +143 -0
- package/dist/runtime/display/components/blocks/display-code.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-date.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-date.vue +174 -0
- package/dist/runtime/display/components/blocks/display-date.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-diff.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-diff.vue +142 -0
- package/dist/runtime/display/components/blocks/display-diff.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-empty.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-empty.vue +74 -0
- package/dist/runtime/display/components/blocks/display-empty.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-facts.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-facts.vue +180 -0
- package/dist/runtime/display/components/blocks/display-facts.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-heading.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-heading.vue +96 -0
- package/dist/runtime/display/components/blocks/display-heading.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-list.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-list.vue +295 -0
- package/dist/runtime/display/components/blocks/display-list.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-media.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-media.vue +326 -0
- package/dist/runtime/display/components/blocks/display-media.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-meter.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-meter.vue +301 -0
- package/dist/runtime/display/components/blocks/display-meter.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-person.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-person.vue +146 -0
- package/dist/runtime/display/components/blocks/display-person.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-record.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-record.vue +208 -0
- package/dist/runtime/display/components/blocks/display-record.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-stat.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-stat.vue +178 -0
- package/dist/runtime/display/components/blocks/display-stat.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-table.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-table.vue +132 -0
- package/dist/runtime/display/components/blocks/display-table.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-text.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-text.vue +135 -0
- package/dist/runtime/display/components/blocks/display-text.vue.d.ts +4 -0
- package/dist/runtime/display/components/blocks/display-timeline.d.vue.ts +4 -0
- package/dist/runtime/display/components/blocks/display-timeline.vue +232 -0
- package/dist/runtime/display/components/blocks/display-timeline.vue.d.ts +4 -0
- package/dist/runtime/display/components/display.d.vue.ts +24 -0
- package/dist/runtime/display/components/display.vue +21 -0
- package/dist/runtime/display/components/display.vue.d.ts +24 -0
- package/dist/runtime/display/components/parts/display-actions.d.vue.ts +9 -0
- package/dist/runtime/display/components/parts/display-actions.vue +24 -0
- package/dist/runtime/display/components/parts/display-actions.vue.d.ts +9 -0
- package/dist/runtime/display/components/parts/display-chip.d.vue.ts +11 -0
- package/dist/runtime/display/components/parts/display-chip.vue +45 -0
- package/dist/runtime/display/components/parts/display-chip.vue.d.ts +11 -0
- package/dist/runtime/display/components/parts/display-copy.d.vue.ts +6 -0
- package/dist/runtime/display/components/parts/display-copy.vue +27 -0
- package/dist/runtime/display/components/parts/display-copy.vue.d.ts +6 -0
- package/dist/runtime/display/components/parts/display-empty-line.d.vue.ts +7 -0
- package/dist/runtime/display/components/parts/display-empty-line.vue +21 -0
- package/dist/runtime/display/components/parts/display-empty-line.vue.d.ts +7 -0
- package/dist/runtime/display/components/parts/display-fact-value.d.vue.ts +7 -0
- package/dist/runtime/display/components/parts/display-fact-value.vue +35 -0
- package/dist/runtime/display/components/parts/display-fact-value.vue.d.ts +7 -0
- package/dist/runtime/display/components/parts/display-load-error.d.vue.ts +7 -0
- package/dist/runtime/display/components/parts/display-load-error.vue +29 -0
- package/dist/runtime/display/components/parts/display-load-error.vue.d.ts +7 -0
- package/dist/runtime/display/components/parts/display-more.d.vue.ts +10 -0
- package/dist/runtime/display/components/parts/display-more.vue +23 -0
- package/dist/runtime/display/components/parts/display-more.vue.d.ts +10 -0
- package/dist/runtime/display/components/parts/display-record-media.d.vue.ts +9 -0
- package/dist/runtime/display/components/parts/display-record-media.vue +59 -0
- package/dist/runtime/display/components/parts/display-record-media.vue.d.ts +9 -0
- package/dist/runtime/display/components/parts/display-render.d.ts +10 -0
- package/dist/runtime/display/components/parts/display-render.js +5 -0
- package/dist/runtime/display/components/parts/display-skeleton.d.vue.ts +7 -0
- package/dist/runtime/display/components/parts/display-skeleton.vue +67 -0
- package/dist/runtime/display/components/parts/display-skeleton.vue.d.ts +7 -0
- package/dist/runtime/display/composables/use-display-config.d.ts +6 -0
- package/dist/runtime/display/composables/use-display-config.js +11 -0
- package/dist/runtime/display/composables/use-display-file-preview.d.ts +9 -0
- package/dist/runtime/display/composables/use-display-file-preview.js +11 -0
- package/dist/runtime/display/composables/use-display-icons.d.ts +8 -0
- package/dist/runtime/display/composables/use-display-icons.js +62 -0
- package/dist/runtime/display/composables/use-display-locale.d.ts +5 -0
- package/dist/runtime/display/composables/use-display-locale.js +5 -0
- package/dist/runtime/display/composables/use-display-number.d.ts +2 -0
- package/dist/runtime/display/composables/use-display-number.js +7 -0
- package/dist/runtime/display/composables/use-display-reveal.d.ts +14 -0
- package/dist/runtime/display/composables/use-display-reveal.js +15 -0
- package/dist/runtime/display/composables/use-display-url.d.ts +26 -0
- package/dist/runtime/display/composables/use-display-url.js +46 -0
- package/dist/runtime/display/index.d.ts +4 -0
- package/dist/runtime/display/index.js +3 -0
- package/dist/runtime/display/plugins/display-tones.d.ts +3 -0
- package/dist/runtime/display/plugins/display-tones.js +17 -0
- package/dist/runtime/display/types/blocks/accordion.d.ts +18 -0
- package/dist/runtime/display/types/blocks/accordion.js +0 -0
- package/dist/runtime/display/types/blocks/alert.d.ts +31 -0
- package/dist/runtime/display/types/blocks/alert.js +0 -0
- package/dist/runtime/display/types/blocks/breakdown.d.ts +36 -0
- package/dist/runtime/display/types/blocks/breakdown.js +0 -0
- package/dist/runtime/display/types/blocks/chart.d.ts +23 -0
- package/dist/runtime/display/types/blocks/chart.js +0 -0
- package/dist/runtime/display/types/blocks/code.d.ts +21 -0
- package/dist/runtime/display/types/blocks/code.js +0 -0
- package/dist/runtime/display/types/blocks/date.d.ts +27 -0
- package/dist/runtime/display/types/blocks/date.js +0 -0
- package/dist/runtime/display/types/blocks/diff.d.ts +20 -0
- package/dist/runtime/display/types/blocks/diff.js +0 -0
- package/dist/runtime/display/types/blocks/empty.d.ts +13 -0
- package/dist/runtime/display/types/blocks/empty.js +0 -0
- package/dist/runtime/display/types/blocks/facts.d.ts +50 -0
- package/dist/runtime/display/types/blocks/facts.js +0 -0
- package/dist/runtime/display/types/blocks/heading.d.ts +19 -0
- package/dist/runtime/display/types/blocks/heading.js +0 -0
- package/dist/runtime/display/types/blocks/list.d.ts +36 -0
- package/dist/runtime/display/types/blocks/list.js +0 -0
- package/dist/runtime/display/types/blocks/media.d.ts +42 -0
- package/dist/runtime/display/types/blocks/media.js +0 -0
- package/dist/runtime/display/types/blocks/meter.d.ts +48 -0
- package/dist/runtime/display/types/blocks/meter.js +0 -0
- package/dist/runtime/display/types/blocks/person.d.ts +32 -0
- package/dist/runtime/display/types/blocks/person.js +0 -0
- package/dist/runtime/display/types/blocks/record.d.ts +42 -0
- package/dist/runtime/display/types/blocks/record.js +0 -0
- package/dist/runtime/display/types/blocks/stat.d.ts +44 -0
- package/dist/runtime/display/types/blocks/stat.js +0 -0
- package/dist/runtime/display/types/blocks/table.d.ts +28 -0
- package/dist/runtime/display/types/blocks/table.js +0 -0
- package/dist/runtime/display/types/blocks/text.d.ts +22 -0
- package/dist/runtime/display/types/blocks/text.js +0 -0
- package/dist/runtime/display/types/blocks/timeline.d.ts +37 -0
- package/dist/runtime/display/types/blocks/timeline.js +0 -0
- package/dist/runtime/display/types/config.d.ts +53 -0
- package/dist/runtime/display/types/config.js +0 -0
- package/dist/runtime/display/types/index.d.ts +22 -0
- package/dist/runtime/display/types/index.js +0 -0
- package/dist/runtime/display/types/registry.d.ts +57 -0
- package/dist/runtime/display/types/registry.js +0 -0
- package/dist/runtime/display/types/shared.d.ts +71 -0
- package/dist/runtime/display/types/shared.js +0 -0
- package/dist/runtime/display/utils/classes.d.ts +3 -0
- package/dist/runtime/display/utils/classes.js +7 -0
- package/dist/runtime/display/utils/config.d.ts +6 -0
- package/dist/runtime/display/utils/config.js +7 -0
- package/dist/runtime/display/utils/content.d.ts +7 -0
- package/dist/runtime/display/utils/content.js +13 -0
- package/dist/runtime/display/utils/dates.d.ts +16 -0
- package/dist/runtime/display/utils/dates.js +34 -0
- package/dist/runtime/display/utils/json.d.ts +7 -0
- package/dist/runtime/display/utils/json.js +15 -0
- package/dist/runtime/display/utils/meter.d.ts +7 -0
- package/dist/runtime/display/utils/meter.js +13 -0
- package/dist/runtime/display/utils/registry.d.ts +47 -0
- package/dist/runtime/display/utils/registry.js +64 -0
- package/dist/runtime/display/utils/tone-css.d.ts +7 -0
- package/dist/runtime/display/utils/tone-css.js +38 -0
- package/dist/runtime/display/utils/tone.d.ts +26 -0
- package/dist/runtime/display/utils/tone.js +38 -0
- package/dist/runtime/display/utils/urls.d.ts +10 -0
- package/dist/runtime/display/utils/urls.js +33 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +5 -3
- package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +7 -0
- package/dist/runtime/file-preview/composables/use-file-preview-source.js +7 -1
- package/dist/runtime/file-preview/index.d.ts +1 -0
- package/dist/runtime/file-preview/index.js +1 -0
- package/dist/runtime/file-preview/types/file.d.ts +9 -0
- package/dist/runtime/file-preview/utils/errors.d.ts +30 -0
- package/dist/runtime/file-preview/utils/errors.js +26 -0
- package/dist/runtime/form/components/utils/form-file-preview.vue +7 -2
- package/dist/runtime/form/composables/use-form-root.js +2 -0
- package/dist/runtime/form/composables/use-form-runtime.js +7 -2
- package/dist/runtime/form/fields/index.d.ts +5 -0
- package/dist/runtime/form/fields/info/component.vue +51 -19
- package/dist/runtime/form/fields/info/config.d.ts +5 -0
- package/dist/runtime/form/fields/info/config.js +2 -1
- package/dist/runtime/form/fields/info/types.d.ts +78 -4
- package/dist/runtime/form/fields/info/use-info-display.d.ts +20 -0
- package/dist/runtime/form/fields/info/use-info-display.js +82 -0
- package/dist/runtime/form/fields/upload/upload-item.vue +10 -2
- package/dist/runtime/form/types/api.d.ts +6 -0
- package/dist/runtime/form/types/runtime.d.ts +2 -0
- package/dist/runtime/form/utils/dynamic.d.ts +6 -0
- package/dist/runtime/form/utils/dynamic.js +7 -0
- package/dist/runtime/i18n/locales/en.js +32 -0
- package/dist/runtime/i18n/locales/fr.js +32 -0
- package/dist/runtime/i18n/types.d.ts +40 -0
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/utils/file.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-root.vue +2 -0
- package/dist/runtime/table/components/data-list/data-list-selection-actions.d.vue.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-selection-actions.vue +32 -5
- package/dist/runtime/table/components/data-list/data-list-selection-actions.vue.d.ts +1 -1
- package/dist/runtime/table/components/data-list/data-list-selection-sheet.d.vue.ts +10 -0
- package/dist/runtime/table/components/data-list/data-list-selection-sheet.vue +238 -0
- package/dist/runtime/table/components/data-list/data-list-selection-sheet.vue.d.ts +10 -0
- package/dist/runtime/table/components/grid/grid-renderer.vue +2 -0
- package/dist/runtime/table/components/summary/summary-renderer.vue +2 -0
- package/dist/runtime/table/components/table/table-renderer.vue +2 -0
- package/dist/runtime/table/components/utils/overlay-inset-spacer.d.vue.ts +3 -0
- package/dist/runtime/table/components/utils/overlay-inset-spacer.vue +13 -0
- package/dist/runtime/table/components/utils/overlay-inset-spacer.vue.d.ts +3 -0
- package/dist/runtime/table/composables/use-data-list-overlay-inset.d.ts +10 -0
- package/dist/runtime/table/composables/use-data-list-overlay-inset.js +11 -0
- package/dist/runtime/table/composables/use-table-actions.js +11 -2
- package/dist/runtime/table/constants/mobile.d.ts +0 -7
- package/dist/runtime/table/constants/mobile.js +0 -1
- package/dist/runtime/table/types/actions.d.ts +7 -0
- package/dist/runtime/table/types/data-list.d.ts +8 -1
- package/dist/runtime/table/utils/data-list-ui.d.ts +1 -1
- package/package.json +16 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const BUILTIN_TONES = [
|
|
2
|
+
"primary",
|
|
3
|
+
"secondary",
|
|
4
|
+
"success",
|
|
5
|
+
"info",
|
|
6
|
+
"warning",
|
|
7
|
+
"error",
|
|
8
|
+
"neutral"
|
|
9
|
+
];
|
|
10
|
+
export function displayToneClass(tone) {
|
|
11
|
+
return `nut-display-tone-${tone}`;
|
|
12
|
+
}
|
|
13
|
+
export function isBuiltinTone(tone) {
|
|
14
|
+
return BUILTIN_TONES.some((builtin) => builtin === tone);
|
|
15
|
+
}
|
|
16
|
+
export const DISPLAY_SURFACE_CLASSES = {
|
|
17
|
+
bordered: "rounded-[var(--nut-display-radius)] border border-default",
|
|
18
|
+
elevated: "rounded-[var(--nut-display-radius)] bg-default shadow-[0_10px_28px_-14px_rgb(0_0_0/0.3)] ring ring-default",
|
|
19
|
+
muted: "rounded-[var(--nut-display-radius)] bg-elevated",
|
|
20
|
+
plain: ""
|
|
21
|
+
};
|
|
22
|
+
export const DISPLAY_SURFACE_PADDING = {
|
|
23
|
+
bordered: "p-4",
|
|
24
|
+
elevated: "p-4",
|
|
25
|
+
muted: "p-4",
|
|
26
|
+
plain: ""
|
|
27
|
+
};
|
|
28
|
+
const NAME_TONES = ["primary", "info", "success", "warning", "secondary", "error"];
|
|
29
|
+
export function toneFromName(name) {
|
|
30
|
+
let hash = 0;
|
|
31
|
+
for (const char of name) hash = (hash * 31 + char.charCodeAt(0)) % 2147483647;
|
|
32
|
+
return NAME_TONES[hash % NAME_TONES.length] ?? "primary";
|
|
33
|
+
}
|
|
34
|
+
export function initialsOf(name) {
|
|
35
|
+
const words = name.trim().split(/\s+/u).filter(Boolean);
|
|
36
|
+
const letters = words.length > 1 ? [words[0], words.at(-1)] : [words[0]];
|
|
37
|
+
return letters.map((word) => word?.[0] ?? "").join("").toUpperCase();
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DisplayUrl } from '../types/index.js';
|
|
2
|
+
/** The address of a link, calling it when it is a function. */
|
|
3
|
+
export declare function resolveDisplayUrl(source: DisplayUrl): Promise<DisplayUrl & string>;
|
|
4
|
+
/**
|
|
5
|
+
* Opens a link in a new tab. A function's tab opens during the click, so popup blockers let it
|
|
6
|
+
* through, and follows the address once it resolves; it closes again if the address fails.
|
|
7
|
+
*/
|
|
8
|
+
export declare function openDisplayUrl(source: DisplayUrl): Promise<void>;
|
|
9
|
+
/** Downloads a link under `name`, resolving it first when it is a function. */
|
|
10
|
+
export declare function downloadDisplayUrl(source: DisplayUrl, name?: string): Promise<void>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isString } from "../../shared/utils/predicate.js";
|
|
2
|
+
export async function resolveDisplayUrl(source) {
|
|
3
|
+
return isString(source) ? source : await source();
|
|
4
|
+
}
|
|
5
|
+
export async function openDisplayUrl(source) {
|
|
6
|
+
if (isString(source)) {
|
|
7
|
+
window.open(source, "_blank", "noopener");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const tab = window.open("about:blank", "_blank");
|
|
11
|
+
try {
|
|
12
|
+
const url = await source();
|
|
13
|
+
if (!tab) {
|
|
14
|
+
window.open(url, "_blank", "noopener");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
tab.opener = null;
|
|
18
|
+
tab.location.href = url;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
tab?.close();
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function downloadDisplayUrl(source, name) {
|
|
25
|
+
const anchor = document.createElement("a");
|
|
26
|
+
anchor.href = await resolveDisplayUrl(source);
|
|
27
|
+
anchor.rel = "noopener";
|
|
28
|
+
anchor.target = "_blank";
|
|
29
|
+
if (name) anchor.download = name;
|
|
30
|
+
document.body.append(anchor);
|
|
31
|
+
anchor.click();
|
|
32
|
+
anchor.remove();
|
|
33
|
+
}
|
|
@@ -88,9 +88,11 @@ function retry() {
|
|
|
88
88
|
v-else-if="source.status.value === 'error'"
|
|
89
89
|
icon="i-lucide-triangle-alert"
|
|
90
90
|
tone="error"
|
|
91
|
-
:title="t('filePreview.errors.sourceTitle')"
|
|
92
|
-
:description="
|
|
93
|
-
|
|
91
|
+
:title="source.failure.value?.title ?? t('filePreview.errors.sourceTitle')"
|
|
92
|
+
:description="
|
|
93
|
+
source.failure.value?.description ?? t('filePreview.errors.sourceDescription')
|
|
94
|
+
"
|
|
95
|
+
:detail="source.failure.value?.description ? null : source.error.value?.message"
|
|
94
96
|
>
|
|
95
97
|
<UButton
|
|
96
98
|
v-if="source.retryable.value"
|
|
@@ -8,6 +8,13 @@ type FilePreviewSourceStatus = 'loading' | 'ready' | 'error';
|
|
|
8
8
|
export declare function useFilePreviewSource(item: () => FilePreviewItem | null): {
|
|
9
9
|
blob: import("vue").ComputedRef<Blob | null>;
|
|
10
10
|
error: import("vue").ShallowRef<Error | null, Error | null>;
|
|
11
|
+
failure: import("vue").ShallowRef<{
|
|
12
|
+
description: string | undefined;
|
|
13
|
+
title: string | undefined;
|
|
14
|
+
} | null, {
|
|
15
|
+
description: string | undefined;
|
|
16
|
+
title: string | undefined;
|
|
17
|
+
} | null>;
|
|
11
18
|
/** Whether `retry()` can fetch the source again, which only a source function can. */
|
|
12
19
|
retryable: import("vue").ComputedRef<boolean>;
|
|
13
20
|
retry: () => Promise<void>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useObjectUrl } from "@vueuse/core";
|
|
2
2
|
import { computed, ref, shallowRef, watch } from "vue";
|
|
3
|
-
import { isFunction, isString } from "../../shared/utils/predicate.js";
|
|
3
|
+
import { isFunction, isObject, isString } from "../../shared/utils/predicate.js";
|
|
4
|
+
import { explainFilePreviewFailure } from "../utils/errors.js";
|
|
4
5
|
const resolved = /* @__PURE__ */ new WeakMap();
|
|
5
6
|
export function useFilePreviewSource(item) {
|
|
6
7
|
const value = shallowRef(null);
|
|
7
8
|
const status = ref("loading");
|
|
8
9
|
const error = shallowRef(null);
|
|
10
|
+
const failure = shallowRef(null);
|
|
9
11
|
const blob = computed(() => value.value instanceof Blob ? value.value : null);
|
|
10
12
|
const objectUrl = useObjectUrl(blob);
|
|
11
13
|
const url = computed(() => isString(value.value) ? value.value : objectUrl.value ?? null);
|
|
@@ -13,6 +15,7 @@ export function useFilePreviewSource(item) {
|
|
|
13
15
|
function settle(next) {
|
|
14
16
|
value.value = next;
|
|
15
17
|
error.value = null;
|
|
18
|
+
failure.value = null;
|
|
16
19
|
status.value = "ready";
|
|
17
20
|
}
|
|
18
21
|
async function resolve(options) {
|
|
@@ -26,6 +29,7 @@ export function useFilePreviewSource(item) {
|
|
|
26
29
|
if (known !== void 0) return settle(known);
|
|
27
30
|
value.value = null;
|
|
28
31
|
error.value = null;
|
|
32
|
+
failure.value = null;
|
|
29
33
|
status.value = "loading";
|
|
30
34
|
try {
|
|
31
35
|
const next = await src();
|
|
@@ -35,6 +39,7 @@ export function useFilePreviewSource(item) {
|
|
|
35
39
|
} catch (cause) {
|
|
36
40
|
if (current !== attempt) return;
|
|
37
41
|
error.value = cause instanceof Error ? cause : new Error(String(cause));
|
|
42
|
+
failure.value = cause instanceof Error || isObject(cause) ? explainFilePreviewFailure(cause) : null;
|
|
38
43
|
status.value = "error";
|
|
39
44
|
}
|
|
40
45
|
}
|
|
@@ -42,6 +47,7 @@ export function useFilePreviewSource(item) {
|
|
|
42
47
|
return {
|
|
43
48
|
blob,
|
|
44
49
|
error,
|
|
50
|
+
failure,
|
|
45
51
|
/** Whether `retry()` can fetch the source again, which only a source function can. */
|
|
46
52
|
retryable: computed(() => isFunction(item()?.file.src)),
|
|
47
53
|
retry: () => resolve({ force: true }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { createFilePreviewApi, filePreviewApiKey, provideFilePreview, useFilePreview, } from './composables/use-file-preview-api.js';
|
|
2
2
|
export { onFilePreviewKey, useFilePreviewShell } from './composables/use-file-preview-shell.js';
|
|
3
3
|
export { FILE_PREVIEW_TEXT_LIMIT, useFilePreviewText } from './composables/use-file-preview-text.js';
|
|
4
|
+
export { FilePreviewSourceError } from './utils/errors.js';
|
|
4
5
|
export { builtInFilePreviewRenderers, defineFilePreviewRenderer, detectFilePreviewKind, matchFilePreview, } from './utils/renderers.js';
|
|
5
6
|
export type * from './types';
|
|
@@ -6,6 +6,7 @@ export {
|
|
|
6
6
|
} from "./composables/use-file-preview-api.js";
|
|
7
7
|
export { onFilePreviewKey, useFilePreviewShell } from "./composables/use-file-preview-shell.js";
|
|
8
8
|
export { FILE_PREVIEW_TEXT_LIMIT, useFilePreviewText } from "./composables/use-file-preview-text.js";
|
|
9
|
+
export { FilePreviewSourceError } from "./utils/errors.js";
|
|
9
10
|
export {
|
|
10
11
|
builtInFilePreviewRenderers,
|
|
11
12
|
defineFilePreviewRenderer,
|
|
@@ -11,6 +11,15 @@ export type FilePreviewSourceValue = string | Blob;
|
|
|
11
11
|
* URLs are requested on demand; it runs again when the user presses Retry after an error.
|
|
12
12
|
*/
|
|
13
13
|
export type FilePreviewSource = FilePreviewSourceValue | (() => MaybePromise<FilePreviewSourceValue>);
|
|
14
|
+
/**
|
|
15
|
+
* Why a source function failed, carried by the error it throws (for example a
|
|
16
|
+
* `FilePreviewSourceError`, or any error with these properties). The preview shows them in place
|
|
17
|
+
* of its generic copy and leaves out the raw error message.
|
|
18
|
+
*/
|
|
19
|
+
export interface FilePreviewSourceFailure {
|
|
20
|
+
title?: FilePreviewText;
|
|
21
|
+
description?: FilePreviewText;
|
|
22
|
+
}
|
|
14
23
|
export interface FilePreviewDetail {
|
|
15
24
|
label: FilePreviewText;
|
|
16
25
|
value: FilePreviewText;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { GenericObject } from '../../shared/types/utils.js';
|
|
2
|
+
import type { FilePreviewSourceFailure, FilePreviewText } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Error a source function throws when the app knows why the file is unavailable. The preview shows
|
|
5
|
+
* its `title` and `description` in place of the generic ones, without the raw error message.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* src: async () => {
|
|
10
|
+
* const response = await fetch(`/api/invoices/${id}/file`)
|
|
11
|
+
* if (response.status === 404)
|
|
12
|
+
* throw new FilePreviewSourceError({ description: 'The invoice file cannot be found.' })
|
|
13
|
+
* return (await response.json()).url
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare class FilePreviewSourceError extends Error implements FilePreviewSourceFailure {
|
|
18
|
+
readonly title?: FilePreviewText;
|
|
19
|
+
readonly description?: FilePreviewText;
|
|
20
|
+
constructor(options: {
|
|
21
|
+
title?: FilePreviewText;
|
|
22
|
+
description?: FilePreviewText;
|
|
23
|
+
message?: string;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** The title and description a thrown source error carries, if any, resolved to text. */
|
|
27
|
+
export declare function explainFilePreviewFailure(cause: Error | GenericObject): {
|
|
28
|
+
description: string | undefined;
|
|
29
|
+
title: string | undefined;
|
|
30
|
+
} | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isFunction, isString } from "../../shared/utils/predicate.js";
|
|
2
|
+
export class FilePreviewSourceError extends Error {
|
|
3
|
+
title;
|
|
4
|
+
description;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
super(
|
|
7
|
+
options.message ?? textOf(options.description) ?? textOf(options.title) ?? "File source failed"
|
|
8
|
+
);
|
|
9
|
+
this.name = "FilePreviewSourceError";
|
|
10
|
+
this.title = options.title;
|
|
11
|
+
this.description = options.description;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function explainFilePreviewFailure(cause) {
|
|
15
|
+
const title = "title" in cause ? textOf(cause.title) : void 0;
|
|
16
|
+
const description = "description" in cause ? textOf(cause.description) : void 0;
|
|
17
|
+
return title === void 0 && description === void 0 ? null : { description, title };
|
|
18
|
+
}
|
|
19
|
+
function textOf(value) {
|
|
20
|
+
if (isString(value)) return value;
|
|
21
|
+
if (isFunction(value)) {
|
|
22
|
+
const text = value();
|
|
23
|
+
return isString(text) ? text : void 0;
|
|
24
|
+
}
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import UButton from "@nuxt/ui/components/Button.vue";
|
|
3
3
|
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
4
|
-
import { computed, onScopeDispose, watch } from "vue";
|
|
4
|
+
import { computed, onScopeDispose, ref, watch } from "vue";
|
|
5
5
|
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
6
6
|
import { fileExtensionLabel, fileIconName, formatFileSize } from "../../../shared/utils/file";
|
|
7
7
|
const props = defineProps({
|
|
@@ -25,6 +25,10 @@ const preview = computed(() => {
|
|
|
25
25
|
return previewUrl;
|
|
26
26
|
});
|
|
27
27
|
const icon = computed(() => fileIconName(props.file.type));
|
|
28
|
+
const previewFailed = ref(false);
|
|
29
|
+
watch(preview, () => {
|
|
30
|
+
previewFailed.value = false;
|
|
31
|
+
});
|
|
28
32
|
const meta = computed(
|
|
29
33
|
() => `${fileExtensionLabel(props.file)} \xB7 ${formatFileSize(props.file.size, code.value)}`
|
|
30
34
|
);
|
|
@@ -50,8 +54,9 @@ onScopeDispose(() => {
|
|
|
50
54
|
data-form-file=""
|
|
51
55
|
>
|
|
52
56
|
<img
|
|
53
|
-
v-if="preview"
|
|
57
|
+
v-if="preview && !previewFailed"
|
|
54
58
|
:src="preview"
|
|
59
|
+
@error="previewFailed = true"
|
|
55
60
|
:alt="file.name"
|
|
56
61
|
class="size-10 shrink-0 rounded-md border border-default object-cover"
|
|
57
62
|
/>
|
|
@@ -37,6 +37,8 @@ export function useFormRoot(params) {
|
|
|
37
37
|
const ownedRuntime = params.runtime?.();
|
|
38
38
|
const runtime = ownedRuntime ?? useFormRuntime({ input, schema, syncInput, validationMode });
|
|
39
39
|
provideFormRuntime(runtime);
|
|
40
|
+
runtime.bindCancel(() => cancel());
|
|
41
|
+
onBeforeUnmount(() => runtime.bindCancel(null));
|
|
40
42
|
const confirm = useFormConfirm();
|
|
41
43
|
let leaveConfirmed = false;
|
|
42
44
|
watch(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, inject, nextTick, provide, ref, watch } from "vue";
|
|
1
|
+
import { computed, inject, nextTick, provide, ref, shallowRef, watch } from "vue";
|
|
2
2
|
import { useUiToolsLocale } from "../../i18n/use-locale.js";
|
|
3
3
|
import { createPublicFormApi } from "../utils/api.js";
|
|
4
4
|
import { resolveFieldDependencies } from "../utils/dependencies.js";
|
|
@@ -44,6 +44,7 @@ export function useFormRuntime(params) {
|
|
|
44
44
|
const currentStepIndex = ref(0);
|
|
45
45
|
const navigationActionPending = ref(null);
|
|
46
46
|
const effects = createEffectLifecycle();
|
|
47
|
+
const cancelHandler = shallowRef(null);
|
|
47
48
|
const { t } = useUiToolsLocale();
|
|
48
49
|
setContext(getSchemaContext(params.schema.value));
|
|
49
50
|
function createFormNamespace(path, read) {
|
|
@@ -64,7 +65,8 @@ export function useFormRuntime(params) {
|
|
|
64
65
|
setError: (target, message, options) => runtime.setError(resolve(target), message, options),
|
|
65
66
|
state: () => read.state(),
|
|
66
67
|
submit: () => runtime.submit(),
|
|
67
|
-
validate: () => runtime.validate()
|
|
68
|
+
validate: () => runtime.validate(),
|
|
69
|
+
close: () => cancelHandler.value?.() ?? Promise.resolve()
|
|
68
70
|
};
|
|
69
71
|
}
|
|
70
72
|
function createApiFactory(read) {
|
|
@@ -449,6 +451,9 @@ export function useFormRuntime(params) {
|
|
|
449
451
|
return result.success;
|
|
450
452
|
},
|
|
451
453
|
submitHandler,
|
|
454
|
+
bindCancel: (cancel) => {
|
|
455
|
+
cancelHandler.value = cancel;
|
|
456
|
+
},
|
|
452
457
|
trackEffect: effects.track,
|
|
453
458
|
validate,
|
|
454
459
|
validateCurrentStep
|
|
@@ -134,6 +134,11 @@ export declare const formFieldKinds: readonly [{
|
|
|
134
134
|
};
|
|
135
135
|
readonly state: "stateless";
|
|
136
136
|
readonly type: "info";
|
|
137
|
+
readonly ui: {
|
|
138
|
+
readonly description: true;
|
|
139
|
+
readonly hint: true;
|
|
140
|
+
readonly label: true;
|
|
141
|
+
};
|
|
137
142
|
}, {
|
|
138
143
|
readonly layout: {
|
|
139
144
|
readonly item: true;
|
|
@@ -1,30 +1,62 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import UAlert from "@nuxt/ui/components/Alert.vue";
|
|
3
2
|
import { computed } from "vue";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import DisplayAlert from "../../../display/components/blocks/display-alert.vue";
|
|
4
|
+
import DisplayBlock from "../../../display/components/display.vue";
|
|
5
|
+
import DisplayLoadError from "../../../display/components/parts/display-load-error.vue";
|
|
6
|
+
import DisplaySkeleton from "../../../display/components/parts/display-skeleton.vue";
|
|
7
|
+
import { DISPLAY_SKELETON_LAYOUTS } from "../../../display/utils/registry";
|
|
8
|
+
import FormFieldShell from "../../components/renderer/form-field-shell.vue";
|
|
9
|
+
import { useFormRuntimeContext } from "../../composables/use-form-runtime";
|
|
10
|
+
import { resolveFormDynamic } from "../../utils/dynamic";
|
|
6
11
|
import { resolveFormText } from "../../utils/text";
|
|
12
|
+
import { useInfoDisplay } from "./use-info-display";
|
|
7
13
|
const props = defineProps({
|
|
8
|
-
field: { type:
|
|
14
|
+
field: { type: null, required: true },
|
|
9
15
|
path: { type: Array, required: true }
|
|
10
16
|
});
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const form = useFormRuntimeContext();
|
|
18
|
+
const params = computed(() => form.getFieldCallbackParams(props.path, props.field));
|
|
19
|
+
const displayField = computed(() => props.field.display === void 0 ? void 0 : props.field);
|
|
20
|
+
const display = useInfoDisplay({
|
|
21
|
+
callbackParams: params,
|
|
22
|
+
field: () => displayField.value,
|
|
23
|
+
path: () => props.path
|
|
24
|
+
});
|
|
25
|
+
const shelled = computed(() => {
|
|
26
|
+
const { description, help, hint, label } = props.field;
|
|
27
|
+
return [description, help, hint, label].some((value) => value !== void 0);
|
|
28
|
+
});
|
|
29
|
+
const Bare = (_, { slots }) => slots.default?.();
|
|
30
|
+
const notice = computed(() => {
|
|
31
|
+
const field = props.field;
|
|
32
|
+
if (field.display !== void 0) return void 0;
|
|
33
|
+
const options = field.props === void 0 ? void 0 : resolveFormDynamic(field.props, params.value);
|
|
34
|
+
return {
|
|
35
|
+
appearance: options?.variant ?? "soft",
|
|
36
|
+
icon: options?.icon === false ? false : options?.icon ?? "i-lucide-info",
|
|
37
|
+
message: resolveFormDynamic(field.content, params.value),
|
|
38
|
+
title: resolveFormText(options?.title),
|
|
39
|
+
tone: options?.color ?? "neutral"
|
|
40
|
+
};
|
|
19
41
|
});
|
|
20
42
|
</script>
|
|
21
43
|
|
|
22
44
|
<template>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
<component :is="shelled ? FormFieldShell : Bare" v-bind="shelled ? { field, path } : {}">
|
|
46
|
+
<template v-if="displayField">
|
|
47
|
+
<DisplaySkeleton
|
|
48
|
+
v-if="display.pending.value"
|
|
49
|
+
:layout="DISPLAY_SKELETON_LAYOUTS[displayField.display]"
|
|
50
|
+
:count="3"
|
|
51
|
+
/>
|
|
52
|
+
<DisplayLoadError v-else-if="display.error.value !== null" @retry="display.retry" />
|
|
53
|
+
<DisplayBlock
|
|
54
|
+
v-else-if="display.props.value"
|
|
55
|
+
:display="displayField.display"
|
|
56
|
+
:props="display.props.value"
|
|
57
|
+
:preset="displayField.preset"
|
|
58
|
+
/>
|
|
59
|
+
</template>
|
|
60
|
+
<DisplayAlert v-else-if="notice" v-bind="notice" />
|
|
61
|
+
</component>
|
|
30
62
|
</template>
|
|
@@ -1,12 +1,86 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { DisplayKind, DisplayPropsMap } from '../../../display/types/index.js';
|
|
2
|
+
import type { FormFieldCallback, FormFieldCallbackParams } from '../../types/callbacks.js';
|
|
3
|
+
import type { FormQueryOptions } from '../../types/context.js';
|
|
4
|
+
import type { FormFieldDescription, FormFieldProps, FormStatelessFieldBase } from '../../types/field-base.js';
|
|
5
|
+
import type { FormDynamic, FormRenderable, FormText } from '../../types/utils.js';
|
|
6
|
+
/** Props of a notice field: its `content` drawn in an alert. */
|
|
4
7
|
export interface FormInfoProps {
|
|
5
8
|
title?: FormText;
|
|
6
9
|
icon?: string | false;
|
|
7
10
|
color?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'error' | 'neutral';
|
|
8
11
|
variant?: 'solid' | 'outline' | 'soft' | 'subtle';
|
|
9
12
|
}
|
|
10
|
-
|
|
13
|
+
interface FormInfoFieldBase<TContext, TDeps> extends Omit<FormStatelessFieldBase<'info', TContext, TDeps>, 'props'> {
|
|
14
|
+
/** Raw dependency paths read before evaluating `props`, `content` and `condition`. */
|
|
15
|
+
dependencies?: readonly (string | readonly [string, string])[];
|
|
16
|
+
/**
|
|
17
|
+
* Draws the field inside the standard label wrapper, like an input, for a read-only value that
|
|
18
|
+
* belongs in the form grid. `description`, `hint` and `help` do the same.
|
|
19
|
+
*/
|
|
20
|
+
label?: FormText;
|
|
21
|
+
description?: FormText | FormFieldDescription;
|
|
22
|
+
hint?: FormText;
|
|
23
|
+
help?: FormText;
|
|
24
|
+
}
|
|
25
|
+
/** A notice: `content` in an alert, styled by `props`. */
|
|
26
|
+
export interface FormInfoNoticeField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> extends FormInfoFieldBase<TContext, TDeps> {
|
|
27
|
+
display?: undefined;
|
|
28
|
+
preset?: undefined;
|
|
11
29
|
content: FormRenderable | FormFieldCallback<FormRenderable, TContext, TDeps>;
|
|
30
|
+
props?: FormFieldProps<FormInfoProps, TContext, TDeps>;
|
|
12
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Props of a display block, or a way to load them: a promise, or TanStack Query options whose
|
|
34
|
+
* data (after `select`) is the props, cached under their `queryKey`.
|
|
35
|
+
*/
|
|
36
|
+
export type FormInfoDisplaySource<TProps> = TProps | Promise<TProps> | FormQueryOptions<TProps>;
|
|
37
|
+
/**
|
|
38
|
+
* A display block: `display` picks the kind and `props` configure it, statically or from the
|
|
39
|
+
* field's dependencies, so the block follows the form values. `props` may also load them: a
|
|
40
|
+
* callback returning a promise or query options runs again when a dependency changes, shows the
|
|
41
|
+
* block's skeleton until they arrive, drops a late answer to an older run, and shows a compact
|
|
42
|
+
* error with a retry button when loading fails.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* {
|
|
47
|
+
* key: 'quote',
|
|
48
|
+
* type: 'info',
|
|
49
|
+
* display: 'breakdown',
|
|
50
|
+
* dependencies: ['seats'],
|
|
51
|
+
* props: ({ deps }) => ({
|
|
52
|
+
* lines: [{ label: 'Licences', detail: `${deps.get<number>('seats')} × 96 €`, value: '…' }],
|
|
53
|
+
* total: { label: 'Total', value: '…' },
|
|
54
|
+
* }),
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* {
|
|
58
|
+
* key: 'accountNote',
|
|
59
|
+
* type: 'info',
|
|
60
|
+
* display: 'text',
|
|
61
|
+
* dependencies: ['accountId'],
|
|
62
|
+
* props: ({ deps }) =>
|
|
63
|
+
* queryOptions({
|
|
64
|
+
* queryKey: ['accounts', deps.get('accountId'), 'note'],
|
|
65
|
+
* queryFn: () => fetchAccountNote(deps.get('accountId')),
|
|
66
|
+
* select: (note) => ({ appearance: 'quote', text: note.body, author: note.author }),
|
|
67
|
+
* }),
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export type FormInfoDisplayField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = {
|
|
72
|
+
[TKind in DisplayKind]: FormInfoFieldBase<TContext, TDeps> & {
|
|
73
|
+
display: TKind;
|
|
74
|
+
/** Name of a preset in `appConfig.nuxtUiTools.display.presets[display]`; `props` override it. */
|
|
75
|
+
preset?: string;
|
|
76
|
+
content?: undefined;
|
|
77
|
+
/**
|
|
78
|
+
* The block's props, or a callback receiving `{ ctx, deps, api }` that returns them, a promise
|
|
79
|
+
* of them, or query options. `api.form` sets values, submits, or closes the form, so a block
|
|
80
|
+
* action can act on it.
|
|
81
|
+
*/
|
|
82
|
+
props: FormDynamic<FormInfoDisplaySource<DisplayPropsMap[TKind]>, FormFieldCallbackParams<TContext, TDeps>>;
|
|
83
|
+
};
|
|
84
|
+
}[DisplayKind];
|
|
85
|
+
export type FormInfoField<TContext = NonNullable<unknown>, TDeps = NonNullable<unknown>> = FormInfoNoticeField<TContext, TDeps> | FormInfoDisplayField<TContext, TDeps>;
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
import type { DisplayKind, DisplayPropsMap } from '../../../display/types/index.js';
|
|
3
|
+
import type { FormFieldCallbackParams } from '../../types/index.js';
|
|
4
|
+
import type { FormInfoDisplayField } from './types.js';
|
|
5
|
+
type DisplayProps = DisplayPropsMap[DisplayKind];
|
|
6
|
+
/**
|
|
7
|
+
* Resolves the props of a display field: static, derived from its dependencies, or loaded through
|
|
8
|
+
* a promise or TanStack Query options. A promise from an older run never overwrites a newer one.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useInfoDisplay(params: {
|
|
11
|
+
field: () => FormInfoDisplayField | undefined;
|
|
12
|
+
callbackParams: ComputedRef<FormFieldCallbackParams>;
|
|
13
|
+
path: () => readonly string[];
|
|
14
|
+
}): {
|
|
15
|
+
error: ComputedRef<unknown>;
|
|
16
|
+
pending: ComputedRef<boolean>;
|
|
17
|
+
props: ComputedRef<DisplayProps | undefined>;
|
|
18
|
+
retry: () => void;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/vue-query";
|
|
2
|
+
import { computed, ref, shallowRef, unref, watch } from "vue";
|
|
3
|
+
import { isPlainObject, isPromise } from "../../../shared/utils/predicate.js";
|
|
4
|
+
import { resolveFormDynamic } from "../../utils/dynamic.js";
|
|
5
|
+
import { isRecord } from "../../utils/path.js";
|
|
6
|
+
export function useInfoDisplay(params) {
|
|
7
|
+
const attempt = ref(0);
|
|
8
|
+
const promiseRun = ref(0);
|
|
9
|
+
const promiseProps = shallowRef(void 0);
|
|
10
|
+
const promisePending = ref(false);
|
|
11
|
+
const promiseError = shallowRef(null);
|
|
12
|
+
const source = computed(() => {
|
|
13
|
+
void attempt.value;
|
|
14
|
+
const field = params.field();
|
|
15
|
+
return field ? resolveFormDynamic(
|
|
16
|
+
field.props,
|
|
17
|
+
params.callbackParams.value
|
|
18
|
+
) : void 0;
|
|
19
|
+
});
|
|
20
|
+
const querySource = computed(() => isQueryOptions(source.value) ? source.value : null);
|
|
21
|
+
const query = useQuery(
|
|
22
|
+
computed(
|
|
23
|
+
() => querySource.value ?? {
|
|
24
|
+
enabled: false,
|
|
25
|
+
queryFn: async () => null,
|
|
26
|
+
queryKey: ["form-info", params.path().join("."), "disabled"]
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
watch(
|
|
31
|
+
source,
|
|
32
|
+
async (current) => {
|
|
33
|
+
if (!isPromise(current)) {
|
|
34
|
+
promiseRun.value += 1;
|
|
35
|
+
promisePending.value = false;
|
|
36
|
+
promiseError.value = null;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const run = promiseRun.value + 1;
|
|
40
|
+
promiseRun.value = run;
|
|
41
|
+
promisePending.value = true;
|
|
42
|
+
promiseError.value = null;
|
|
43
|
+
try {
|
|
44
|
+
const result = await current;
|
|
45
|
+
if (promiseRun.value !== run) return;
|
|
46
|
+
promiseProps.value = isDisplayProps(result) ? result : void 0;
|
|
47
|
+
} catch (caughtError) {
|
|
48
|
+
if (promiseRun.value === run) promiseError.value = caughtError;
|
|
49
|
+
} finally {
|
|
50
|
+
if (promiseRun.value === run) promisePending.value = false;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{ immediate: true }
|
|
54
|
+
);
|
|
55
|
+
const props = computed(() => {
|
|
56
|
+
const current = source.value;
|
|
57
|
+
if (querySource.value) {
|
|
58
|
+
const data = unref(query.data);
|
|
59
|
+
return isDisplayProps(data) ? data : void 0;
|
|
60
|
+
}
|
|
61
|
+
if (isPromise(current)) return promisePending.value ? void 0 : promiseProps.value;
|
|
62
|
+
return isDisplayProps(current) ? current : void 0;
|
|
63
|
+
});
|
|
64
|
+
const pending = computed(() => {
|
|
65
|
+
if (querySource.value) return unref(query.isPending) && unref(query.fetchStatus) !== "idle";
|
|
66
|
+
return promisePending.value;
|
|
67
|
+
});
|
|
68
|
+
const error = computed(
|
|
69
|
+
() => querySource.value ? unref(query.error) ?? null : promiseError.value
|
|
70
|
+
);
|
|
71
|
+
function retry() {
|
|
72
|
+
if (querySource.value) void query.refetch();
|
|
73
|
+
else attempt.value += 1;
|
|
74
|
+
}
|
|
75
|
+
return { error, pending, props, retry };
|
|
76
|
+
}
|
|
77
|
+
function isQueryOptions(value) {
|
|
78
|
+
return isRecord(value) && Array.isArray(Object.getOwnPropertyDescriptor(value, "queryKey")?.value);
|
|
79
|
+
}
|
|
80
|
+
function isDisplayProps(value) {
|
|
81
|
+
return isPlainObject(value) && !isQueryOptions(value);
|
|
82
|
+
}
|