nuxt-ui-tools 1.7.0 → 1.8.1

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.
Files changed (162) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +18 -0
  3. package/dist/runtime/file-preview/components/provider/file-preview-host.d.vue.ts +7 -0
  4. package/dist/runtime/file-preview/components/provider/file-preview-host.vue +147 -0
  5. package/dist/runtime/file-preview/components/provider/file-preview-host.vue.d.ts +7 -0
  6. package/dist/runtime/file-preview/components/provider/file-preview-provider.d.vue.ts +21 -0
  7. package/dist/runtime/file-preview/components/provider/file-preview-provider.vue +23 -0
  8. package/dist/runtime/file-preview/components/provider/file-preview-provider.vue.d.ts +21 -0
  9. package/dist/runtime/file-preview/components/renderers/audio-renderer.d.vue.ts +10 -0
  10. package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +83 -0
  11. package/dist/runtime/file-preview/components/renderers/audio-renderer.vue.d.ts +10 -0
  12. package/dist/runtime/file-preview/components/renderers/csv-renderer.d.vue.ts +10 -0
  13. package/dist/runtime/file-preview/components/renderers/csv-renderer.vue +116 -0
  14. package/dist/runtime/file-preview/components/renderers/csv-renderer.vue.d.ts +10 -0
  15. package/dist/runtime/file-preview/components/renderers/fallback-renderer.d.vue.ts +10 -0
  16. package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue +41 -0
  17. package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue.d.ts +10 -0
  18. package/dist/runtime/file-preview/components/renderers/file-preview-code.d.vue.ts +9 -0
  19. package/dist/runtime/file-preview/components/renderers/file-preview-code.vue +83 -0
  20. package/dist/runtime/file-preview/components/renderers/file-preview-code.vue.d.ts +9 -0
  21. package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.d.vue.ts +3 -0
  22. package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue +14 -0
  23. package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue.d.ts +3 -0
  24. package/dist/runtime/file-preview/components/renderers/image-renderer.d.vue.ts +10 -0
  25. package/dist/runtime/file-preview/components/renderers/image-renderer.vue +182 -0
  26. package/dist/runtime/file-preview/components/renderers/image-renderer.vue.d.ts +10 -0
  27. package/dist/runtime/file-preview/components/renderers/markdown-renderer.d.vue.ts +10 -0
  28. package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue +78 -0
  29. package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue.d.ts +10 -0
  30. package/dist/runtime/file-preview/components/renderers/media-controls.d.vue.ts +10 -0
  31. package/dist/runtime/file-preview/components/renderers/media-controls.vue +145 -0
  32. package/dist/runtime/file-preview/components/renderers/media-controls.vue.d.ts +10 -0
  33. package/dist/runtime/file-preview/components/renderers/pdf-renderer.d.vue.ts +10 -0
  34. package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue +58 -0
  35. package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue.d.ts +10 -0
  36. package/dist/runtime/file-preview/components/renderers/text-renderer.d.vue.ts +10 -0
  37. package/dist/runtime/file-preview/components/renderers/text-renderer.vue +41 -0
  38. package/dist/runtime/file-preview/components/renderers/text-renderer.vue.d.ts +10 -0
  39. package/dist/runtime/file-preview/components/renderers/video-renderer.d.vue.ts +10 -0
  40. package/dist/runtime/file-preview/components/renderers/video-renderer.vue +110 -0
  41. package/dist/runtime/file-preview/components/renderers/video-renderer.vue.d.ts +10 -0
  42. package/dist/runtime/file-preview/components/shell/file-preview-button.d.vue.ts +16 -0
  43. package/dist/runtime/file-preview/components/shell/file-preview-button.vue +40 -0
  44. package/dist/runtime/file-preview/components/shell/file-preview-button.vue.d.ts +16 -0
  45. package/dist/runtime/{form/components/renderer/form-field-error.d.vue.ts → file-preview/components/shell/file-preview-details.d.vue.ts} +4 -2
  46. package/dist/runtime/file-preview/components/shell/file-preview-details.vue +25 -0
  47. package/dist/runtime/{form/components/renderer/form-field-error.vue.d.ts → file-preview/components/shell/file-preview-details.vue.d.ts} +4 -2
  48. package/dist/runtime/file-preview/components/shell/file-preview-header.d.vue.ts +44 -0
  49. package/dist/runtime/file-preview/components/shell/file-preview-header.vue +163 -0
  50. package/dist/runtime/file-preview/components/shell/file-preview-header.vue.d.ts +44 -0
  51. package/dist/runtime/file-preview/components/shell/file-preview-message.d.vue.ts +21 -0
  52. package/dist/runtime/file-preview/components/shell/file-preview-message.vue +49 -0
  53. package/dist/runtime/file-preview/components/shell/file-preview-message.vue.d.ts +21 -0
  54. package/dist/runtime/file-preview/components/shell/file-preview-shell.d.vue.ts +17 -0
  55. package/dist/runtime/file-preview/components/shell/file-preview-shell.vue +280 -0
  56. package/dist/runtime/file-preview/components/shell/file-preview-shell.vue.d.ts +17 -0
  57. package/dist/runtime/file-preview/components/shell/file-preview-stage.d.vue.ts +37 -0
  58. package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +173 -0
  59. package/dist/runtime/file-preview/components/shell/file-preview-stage.vue.d.ts +37 -0
  60. package/dist/runtime/file-preview/components/shell/file-preview-thumb.d.vue.ts +14 -0
  61. package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue +69 -0
  62. package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue.d.ts +14 -0
  63. package/dist/runtime/file-preview/components/shell/file-preview-tools.d.vue.ts +13 -0
  64. package/dist/runtime/file-preview/components/shell/file-preview-tools.vue +10 -0
  65. package/dist/runtime/file-preview/components/shell/file-preview-tools.vue.d.ts +13 -0
  66. package/dist/runtime/file-preview/composables/use-file-preview-api.d.ts +21 -0
  67. package/dist/runtime/file-preview/composables/use-file-preview-api.js +83 -0
  68. package/dist/runtime/file-preview/composables/use-file-preview-shell.d.ts +13 -0
  69. package/dist/runtime/file-preview/composables/use-file-preview-shell.js +30 -0
  70. package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +17 -0
  71. package/dist/runtime/file-preview/composables/use-file-preview-source.js +51 -0
  72. package/dist/runtime/file-preview/composables/use-file-preview-text.d.ts +14 -0
  73. package/dist/runtime/file-preview/composables/use-file-preview-text.js +79 -0
  74. package/dist/runtime/file-preview/index.d.ts +5 -0
  75. package/dist/runtime/file-preview/index.js +14 -0
  76. package/dist/runtime/file-preview/plugins/file-preview-api.d.ts +11 -0
  77. package/dist/runtime/file-preview/plugins/file-preview-api.js +10 -0
  78. package/dist/runtime/file-preview/types/api.d.ts +74 -0
  79. package/dist/runtime/file-preview/types/api.js +0 -0
  80. package/dist/runtime/file-preview/types/file.d.ts +77 -0
  81. package/dist/runtime/file-preview/types/file.js +0 -0
  82. package/dist/runtime/file-preview/types/index.d.ts +4 -0
  83. package/dist/runtime/file-preview/types/index.js +0 -0
  84. package/dist/runtime/file-preview/types/renderer.d.ts +49 -0
  85. package/dist/runtime/file-preview/types/renderer.js +0 -0
  86. package/dist/runtime/file-preview/types/shell.d.ts +12 -0
  87. package/dist/runtime/file-preview/types/shell.js +0 -0
  88. package/dist/runtime/file-preview/utils/container.d.ts +4 -0
  89. package/dist/runtime/file-preview/utils/container.js +10 -0
  90. package/dist/runtime/file-preview/utils/csv.d.ts +17 -0
  91. package/dist/runtime/file-preview/utils/csv.js +53 -0
  92. package/dist/runtime/file-preview/utils/download.d.ts +8 -0
  93. package/dist/runtime/file-preview/utils/download.js +40 -0
  94. package/dist/runtime/file-preview/utils/files.d.ts +7 -0
  95. package/dist/runtime/file-preview/utils/files.js +53 -0
  96. package/dist/runtime/file-preview/utils/format.d.ts +13 -0
  97. package/dist/runtime/file-preview/utils/format.js +40 -0
  98. package/dist/runtime/file-preview/utils/instance.d.ts +9 -0
  99. package/dist/runtime/file-preview/utils/instance.js +57 -0
  100. package/dist/runtime/file-preview/utils/renderers.d.ts +36 -0
  101. package/dist/runtime/file-preview/utils/renderers.js +247 -0
  102. package/dist/runtime/form/components/page/form-page-header.d.vue.ts +3 -1
  103. package/dist/runtime/form/components/page/form-page-header.vue +4 -4
  104. package/dist/runtime/form/components/page/form-page-header.vue.d.ts +3 -1
  105. package/dist/runtime/form/components/page/form-page-navigation.vue +2 -1
  106. package/dist/runtime/form/components/page/form-page-section.vue +15 -7
  107. package/dist/runtime/form/components/page/form-page.vue +0 -1
  108. package/dist/runtime/form/components/utils/form-file-preview.vue +5 -38
  109. package/dist/runtime/form/composables/use-field-control.d.ts +1 -1
  110. package/dist/runtime/form/composables/use-form-page.js +3 -1
  111. package/dist/runtime/form/composables/use-form-runtime.js +3 -0
  112. package/dist/runtime/form/composables/use-form-upload-registry.d.ts +2 -0
  113. package/dist/runtime/form/composables/use-form-upload-registry.js +11 -6
  114. package/dist/runtime/form/composables/use-form-validation.js +5 -1
  115. package/dist/runtime/form/fields/array-list/use-array-items.d.ts +1 -1
  116. package/dist/runtime/form/fields/array-table/array-table-cell.d.vue.ts +15 -0
  117. package/dist/runtime/form/fields/array-table/array-table-cell.vue +58 -0
  118. package/dist/runtime/form/fields/array-table/array-table-cell.vue.d.ts +15 -0
  119. package/dist/runtime/form/fields/array-table/component.vue +42 -26
  120. package/dist/runtime/form/fields/date/types.d.ts +6 -1
  121. package/dist/runtime/form/fields/date-family/component.vue +7 -1
  122. package/dist/runtime/form/fields/date-family/utils.d.ts +8 -1
  123. package/dist/runtime/form/fields/date-family/utils.js +10 -2
  124. package/dist/runtime/form/fields/hidden/types.d.ts +8 -1
  125. package/dist/runtime/form/fields/upload/component.vue +55 -176
  126. package/dist/runtime/form/fields/upload/types.d.ts +68 -3
  127. package/dist/runtime/form/fields/upload/upload-item.d.vue.ts +24 -0
  128. package/dist/runtime/form/fields/upload/upload-item.vue +158 -0
  129. package/dist/runtime/form/fields/upload/upload-item.vue.d.ts +24 -0
  130. package/dist/runtime/form/fields/upload/use-upload-items.d.ts +44 -0
  131. package/dist/runtime/form/fields/upload/use-upload-items.js +317 -0
  132. package/dist/runtime/form/types/callbacks.d.ts +8 -2
  133. package/dist/runtime/form/types/controller.d.ts +6 -3
  134. package/dist/runtime/form/types/field-base.d.ts +5 -5
  135. package/dist/runtime/form/types/field-output-utils.d.ts +12 -1
  136. package/dist/runtime/form/types/field-output.d.ts +1 -1
  137. package/dist/runtime/form/types/field.d.ts +3 -2
  138. package/dist/runtime/form/types/form-api.d.ts +4 -2
  139. package/dist/runtime/form/types/options-runtime.d.ts +8 -4
  140. package/dist/runtime/form/types/options.d.ts +11 -11
  141. package/dist/runtime/form/types/output.d.ts +33 -10
  142. package/dist/runtime/form/types/page.d.ts +7 -3
  143. package/dist/runtime/form/types/transform.d.ts +5 -2
  144. package/dist/runtime/form/types/ui.d.ts +7 -3
  145. package/dist/runtime/form/types/upload-runtime.d.ts +4 -0
  146. package/dist/runtime/form/utils/page.js +24 -5
  147. package/dist/runtime/form/utils/state.js +14 -11
  148. package/dist/runtime/i18n/locales/en.js +104 -1
  149. package/dist/runtime/i18n/locales/fr.js +104 -1
  150. package/dist/runtime/i18n/types.d.ts +121 -0
  151. package/dist/runtime/query-state/composables.d.ts +2 -2
  152. package/dist/runtime/shared/styles/tokens.css +1 -1
  153. package/dist/runtime/shared/types/utils.d.ts +12 -4
  154. package/dist/runtime/shared/utils/file.d.ts +11 -0
  155. package/dist/runtime/shared/utils/file.js +50 -0
  156. package/dist/runtime/table/components/table/table-renderer.vue +23 -3
  157. package/dist/runtime/table/components/table/table-skeleton-cell.js +2 -2
  158. package/dist/runtime/table/components/utils/search-query-input.d.vue.ts +5 -1
  159. package/dist/runtime/table/components/utils/search-query-input.vue +17 -3
  160. package/dist/runtime/table/components/utils/search-query-input.vue.d.ts +5 -1
  161. package/package.json +56 -29
  162. package/dist/runtime/form/components/renderer/form-field-error.vue +0 -16
@@ -0,0 +1,21 @@
1
+ type __VLS_Props = {
2
+ icon: string;
3
+ title: string;
4
+ description: string;
5
+ badge?: string | null;
6
+ detail?: string | null;
7
+ tone?: 'neutral' | 'error';
8
+ };
9
+ declare var __VLS_6: {};
10
+ type __VLS_Slots = {} & {
11
+ default?: (props: typeof __VLS_6) => any;
12
+ };
13
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
17
+ type __VLS_WithSlots<T, S> = T & {
18
+ new (): {
19
+ $slots: S;
20
+ };
21
+ };
@@ -0,0 +1,49 @@
1
+ <script setup>
2
+ import UIcon from "@nuxt/ui/components/Icon.vue";
3
+ const {
4
+ badge = null,
5
+ detail = null,
6
+ tone = "neutral"
7
+ } = defineProps({
8
+ icon: { type: String, required: true },
9
+ title: { type: String, required: true },
10
+ description: { type: String, required: true },
11
+ badge: { type: [String, null], required: false },
12
+ detail: { type: [String, null], required: false },
13
+ tone: { type: String, required: false }
14
+ });
15
+ </script>
16
+
17
+ <template>
18
+ <div
19
+ class="flex size-full flex-col items-center justify-center gap-2.5 p-8 text-center"
20
+ data-file-preview-message=""
21
+ :data-tone="tone"
22
+ >
23
+ <span
24
+ :class="[
25
+ 'relative mb-1.5 grid h-[76px] w-16 place-items-center rounded-[6px_16px_6px_6px] border bg-default shadow-[0_10px_24px_-14px_rgba(0,0,0,0.35)]',
26
+ tone === 'error' ? 'border-error/40 text-error' : 'border-accented text-muted'
27
+ ]"
28
+ aria-hidden="true"
29
+ >
30
+ <UIcon :name="icon" class="size-7" />
31
+ <span
32
+ v-if="badge"
33
+ class="absolute bottom-2.5 -left-2 rounded bg-inverted px-1.5 py-1 font-mono text-[10px] leading-none font-semibold tracking-wide text-inverted"
34
+ >
35
+ {{ badge }}
36
+ </span>
37
+ </span>
38
+ <p class="m-0 max-w-[36ch] text-[15px] font-semibold text-balance text-highlighted">
39
+ {{ title }}
40
+ </p>
41
+ <p class="m-0 max-w-[44ch] text-sm text-muted">{{ description }}</p>
42
+ <p v-if="detail" class="m-0 max-w-[48ch] font-mono text-xs break-all text-dimmed">
43
+ {{ detail }}
44
+ </p>
45
+ <div v-if="$slots.default" class="mt-2 flex flex-wrap justify-center gap-2">
46
+ <slot />
47
+ </div>
48
+ </div>
49
+ </template>
@@ -0,0 +1,21 @@
1
+ type __VLS_Props = {
2
+ icon: string;
3
+ title: string;
4
+ description: string;
5
+ badge?: string | null;
6
+ detail?: string | null;
7
+ tone?: 'neutral' | 'error';
8
+ };
9
+ declare var __VLS_6: {};
10
+ type __VLS_Slots = {} & {
11
+ default?: (props: typeof __VLS_6) => any;
12
+ };
13
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
17
+ type __VLS_WithSlots<T, S> = T & {
18
+ new (): {
19
+ $slots: S;
20
+ };
21
+ };
@@ -0,0 +1,17 @@
1
+ import type { FilePreviewContainer, FilePreviewInstance } from '../../types/index.js';
2
+ type __VLS_Props = {
3
+ instance: FilePreviewInstance;
4
+ container: FilePreviewContainer;
5
+ narrow: boolean;
6
+ canExpand: boolean;
7
+ expanded: boolean;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ expand: () => any;
11
+ close: () => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onExpand?: (() => any) | undefined;
14
+ onClose?: (() => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,280 @@
1
+ <script setup>
2
+ import { useEventListener, useSwipe } from "@vueuse/core";
3
+ import {
4
+ computed,
5
+ inject,
6
+ nextTick,
7
+ onMounted,
8
+ provide,
9
+ ref,
10
+ shallowRef,
11
+ useTemplateRef,
12
+ watch
13
+ } from "vue";
14
+ import { routerKey } from "vue-router";
15
+ import { useUiToolsLocale } from "../../../i18n/use-locale";
16
+ import { formatFileSize } from "../../../shared/utils/file";
17
+ import { useFilePreview } from "../../composables/use-file-preview-api";
18
+ import {
19
+ filePreviewMarkdownKey,
20
+ filePreviewShellKey,
21
+ isFilePreviewControlKey
22
+ } from "../../composables/use-file-preview-shell";
23
+ import { useFilePreviewSource } from "../../composables/use-file-preview-source";
24
+ import { downloadFilePreviewItem, openFilePreviewItem } from "../../utils/download";
25
+ import { formatFilePreviewDate, resolveFilePreviewText } from "../../utils/format";
26
+ import { filePreviewKindLabel, findFilePreviewRenderer } from "../../utils/renderers";
27
+ import FilePreviewButton from "./file-preview-button.vue";
28
+ import FilePreviewDetails from "./file-preview-details.vue";
29
+ import FilePreviewHeader from "./file-preview-header.vue";
30
+ import FilePreviewStage from "./file-preview-stage.vue";
31
+ import FilePreviewThumb from "./file-preview-thumb.vue";
32
+ const props = defineProps({
33
+ instance: { type: Object, required: true },
34
+ container: { type: String, required: true },
35
+ narrow: { type: Boolean, required: true },
36
+ canExpand: { type: Boolean, required: true },
37
+ expanded: { type: Boolean, required: true }
38
+ });
39
+ const emit = defineEmits(["close", "expand"]);
40
+ const { code, t } = useUiToolsLocale();
41
+ const api = useFilePreview();
42
+ const router = inject(routerKey, null);
43
+ const root = useTemplateRef("root");
44
+ const tools = useTemplateRef("tools");
45
+ const stage = useTemplateRef("stage");
46
+ const strip = useTemplateRef("strip");
47
+ const showDetails = ref(false);
48
+ const reported = shallowRef([]);
49
+ const files = computed(() => props.instance.files.value);
50
+ const index = computed(() => props.instance.index.value);
51
+ const loop = computed(() => props.instance.options.loop === true);
52
+ const item = computed(() => files.value[index.value] ?? null);
53
+ const display = computed(() => item.value?.rendition ?? item.value);
54
+ const renderer = computed(
55
+ () => findFilePreviewRenderer(display.value?.kind ?? "other", api.renderers.value)
56
+ );
57
+ const itemRenderer = computed(
58
+ () => findFilePreviewRenderer(item.value?.kind ?? "other", api.renderers.value)
59
+ );
60
+ const kindLabel = computed(() => filePreviewKindLabel(itemRenderer.value, t));
61
+ const source = useFilePreviewSource(() => display.value);
62
+ const many = computed(() => files.value.length > 1);
63
+ const hasPrevious = computed(() => loop.value || index.value > 0);
64
+ const hasNext = computed(() => loop.value || index.value < files.value.length - 1);
65
+ const showStrip = computed(
66
+ () => many.value && !props.narrow && props.instance.options.gallery !== "counter"
67
+ );
68
+ const arrows = computed(
69
+ () => many.value && !props.narrow && props.container !== "drawer" && ["image", "video"].includes(display.value?.kind ?? "") ? { next: hasNext.value, previous: hasPrevious.value } : null
70
+ );
71
+ const meta = computed(() => {
72
+ const current = item.value;
73
+ if (!current) return "";
74
+ return [
75
+ props.narrow && many.value ? t("filePreview.counter", { current: index.value + 1, total: files.value.length }) : null,
76
+ kindLabel.value,
77
+ current.size === null ? null : formatFileSize(current.size, code.value),
78
+ current.updatedAt ? formatFilePreviewDate(current.updatedAt, code.value) : null
79
+ ].filter(Boolean).join(" \xB7 ");
80
+ });
81
+ const details = computed(() => {
82
+ const current = item.value;
83
+ if (!current) return [];
84
+ const rows = [
85
+ { label: t("filePreview.fields.name"), value: current.name || t("filePreview.untitled") },
86
+ { label: t("filePreview.fields.type"), value: current.mime ?? kindLabel.value },
87
+ current.size === null ? null : {
88
+ label: t("filePreview.fields.size"),
89
+ value: `${formatFileSize(current.size, code.value)} (${current.size.toLocaleString(code.value)} B)`
90
+ },
91
+ current.updatedAt ? {
92
+ label: t("filePreview.fields.modified"),
93
+ value: formatFilePreviewDate(current.updatedAt, code.value)
94
+ } : null
95
+ ];
96
+ const extra = [...reported.value, ...current.file.details ?? []].map((detail) => ({
97
+ label: resolveFilePreviewText(detail.label),
98
+ value: resolveFilePreviewText(detail.value)
99
+ }));
100
+ return [...rows.filter((row) => row !== null), ...extra];
101
+ });
102
+ const announcement = computed(
103
+ () => many.value && item.value ? t("filePreview.position", {
104
+ current: index.value + 1,
105
+ name: item.value.name || t("filePreview.untitled"),
106
+ total: files.value.length
107
+ }) : ""
108
+ );
109
+ provide(filePreviewShellKey, {
110
+ markdown: inject(filePreviewMarkdownKey, null),
111
+ narrow: computed(() => props.narrow),
112
+ options: props.instance.options,
113
+ root,
114
+ tools
115
+ });
116
+ watch(
117
+ () => item.value?.key,
118
+ async () => {
119
+ reported.value = [];
120
+ await nextTick();
121
+ const row = strip.value;
122
+ const thumb = row?.querySelector('[aria-current="true"]');
123
+ if (!row || !(thumb instanceof HTMLElement)) return;
124
+ row.scrollTo({
125
+ behavior: "smooth",
126
+ left: thumb.offsetLeft - (row.clientWidth - thumb.offsetWidth) / 2
127
+ });
128
+ },
129
+ { immediate: true }
130
+ );
131
+ onMounted(() => root.value?.focus({ preventScroll: true }));
132
+ useEventListener(root, "keydown", (event) => {
133
+ if (isFilePreviewControlKey(event)) return;
134
+ const moves = /* @__PURE__ */ new Map([
135
+ ["ArrowLeft", props.instance.handle.previous],
136
+ ["ArrowRight", props.instance.handle.next],
137
+ ["End", () => props.instance.handle.goTo(files.value.length - 1)],
138
+ ["Home", () => props.instance.handle.goTo(0)]
139
+ ]);
140
+ const move = moves.get(event.key);
141
+ if (!move || !many.value) return;
142
+ event.preventDefault();
143
+ move();
144
+ });
145
+ useSwipe(stage, {
146
+ onSwipeEnd: (event, direction) => {
147
+ if (!props.narrow || !many.value) return;
148
+ if (event.target instanceof Element && event.target.closest("[data-file-preview-zoomed]"))
149
+ return;
150
+ if (direction === "left") props.instance.handle.next();
151
+ if (direction === "right") props.instance.handle.previous();
152
+ },
153
+ threshold: 60
154
+ });
155
+ function download() {
156
+ if (item.value) void downloadFilePreviewItem(item.value);
157
+ }
158
+ function openInTab() {
159
+ const url = source.url.value;
160
+ if (url) window.open(url, "_blank", "noopener");
161
+ else if (display.value) void openFilePreviewItem(display.value);
162
+ }
163
+ async function runAction(action) {
164
+ if (!item.value) return;
165
+ if (action.onSelect) await action.onSelect(item.value.file);
166
+ if (!action.to) return;
167
+ if (/^[a-z][\d+.a-z-]*:/iu.test(action.to) || !router) {
168
+ window.open(action.to, "_blank", "noopener");
169
+ return;
170
+ }
171
+ emit("close");
172
+ await router.push(action.to);
173
+ }
174
+ </script>
175
+
176
+ <template>
177
+ <div
178
+ ref="root"
179
+ class="relative flex size-full min-h-0 flex-col overflow-hidden bg-default outline-none"
180
+ tabindex="-1"
181
+ data-file-preview=""
182
+ data-vaul-no-drag=""
183
+ :data-container="container"
184
+ >
185
+ <FilePreviewHeader
186
+ v-if="item"
187
+ :item="item"
188
+ :icon="itemRenderer.icon"
189
+ :meta="meta"
190
+ :index="index"
191
+ :total="files.length"
192
+ :loop="loop"
193
+ :narrow="narrow"
194
+ :can-expand="canExpand"
195
+ :expanded="expanded"
196
+ :details="showDetails"
197
+ @previous="instance.handle.previous"
198
+ @next="instance.handle.next"
199
+ @close="emit('close')"
200
+ @expand="emit('expand')"
201
+ @details="showDetails = !showDetails"
202
+ @download="download"
203
+ @open="openInTab"
204
+ @action="runAction"
205
+ >
206
+ <template #tools>
207
+ <div
208
+ ref="tools"
209
+ class="flex shrink-0 items-center gap-0.5 empty:hidden"
210
+ data-file-preview-tools=""
211
+ />
212
+ <span
213
+ class="mx-1 h-5 w-px shrink-0 bg-(--ui-border) [:empty+&]:hidden"
214
+ aria-hidden="true"
215
+ />
216
+ </template>
217
+ </FilePreviewHeader>
218
+
219
+ <div ref="stage" class="flex min-h-0 flex-1 flex-col">
220
+ <FilePreviewStage
221
+ v-if="item && display"
222
+ :item="item"
223
+ :display="display"
224
+ :renderer="renderer"
225
+ :source="source"
226
+ :container="container"
227
+ :kind-label="kindLabel"
228
+ :arrows="arrows"
229
+ @ready="reported = $event"
230
+ @previous="instance.handle.previous"
231
+ @next="instance.handle.next"
232
+ @download="download"
233
+ >
234
+ <FilePreviewDetails v-if="showDetails" :rows="details" />
235
+ </FilePreviewStage>
236
+ </div>
237
+
238
+ <div
239
+ v-if="narrow"
240
+ class="flex shrink-0 items-center justify-between gap-2 border-t border-default bg-default px-2 pt-1.5 pb-[calc(0.375rem+env(safe-area-inset-bottom,0px))]"
241
+ data-file-preview-bottom=""
242
+ :data-single="many ? void 0 : ''"
243
+ >
244
+ <FilePreviewButton
245
+ v-if="many"
246
+ icon="i-lucide-chevron-left"
247
+ :label="t('filePreview.previous')"
248
+ :disabled="!hasPrevious"
249
+ @click="instance.handle.previous"
250
+ />
251
+ <div ref="tools" class="mx-auto flex items-center gap-0.5" data-file-preview-tools="" />
252
+ <FilePreviewButton
253
+ v-if="many"
254
+ icon="i-lucide-chevron-right"
255
+ :label="t('filePreview.next')"
256
+ :disabled="!hasNext"
257
+ @click="instance.handle.next"
258
+ />
259
+ </div>
260
+
261
+ <div
262
+ v-else-if="showStrip"
263
+ ref="strip"
264
+ class="relative flex shrink-0 gap-1.5 overflow-x-auto border-t border-default bg-default px-3 py-2"
265
+ data-file-preview-strip=""
266
+ >
267
+ <FilePreviewThumb
268
+ v-for="(entry, position) in files"
269
+ :key="entry.key"
270
+ :item="entry"
271
+ :icon="findFilePreviewRenderer(entry.kind, api.renderers.value).icon"
272
+ :current="position === index"
273
+ :label="`${position + 1}. ${entry.name || t('filePreview.untitled')}`"
274
+ @select="instance.handle.goTo(position)"
275
+ />
276
+ </div>
277
+
278
+ <p class="sr-only" aria-live="polite">{{ announcement }}</p>
279
+ </div>
280
+ </template>
@@ -0,0 +1,17 @@
1
+ import type { FilePreviewContainer, FilePreviewInstance } from '../../types/index.js';
2
+ type __VLS_Props = {
3
+ instance: FilePreviewInstance;
4
+ container: FilePreviewContainer;
5
+ narrow: boolean;
6
+ canExpand: boolean;
7
+ expanded: boolean;
8
+ };
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ expand: () => any;
11
+ close: () => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ onExpand?: (() => any) | undefined;
14
+ onClose?: (() => any) | undefined;
15
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
16
+ declare const _default: typeof __VLS_export;
17
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import type { useFilePreviewSource } from '../../composables/use-file-preview-source.js';
2
+ import type { FilePreviewContainer, FilePreviewDetail, FilePreviewItem, FilePreviewRendererDefinition } from '../../types/index.js';
3
+ type __VLS_Props = {
4
+ item: FilePreviewItem;
5
+ display: FilePreviewItem;
6
+ renderer: FilePreviewRendererDefinition;
7
+ source: ReturnType<typeof useFilePreviewSource>;
8
+ container: FilePreviewContainer;
9
+ kindLabel: string;
10
+ arrows: {
11
+ previous: boolean;
12
+ next: boolean;
13
+ } | null;
14
+ };
15
+ declare var __VLS_64: {};
16
+ type __VLS_Slots = {} & {
17
+ default?: (props: typeof __VLS_64) => any;
18
+ };
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
+ ready: (details: readonly FilePreviewDetail[]) => any;
21
+ previous: () => any;
22
+ download: () => any;
23
+ next: () => any;
24
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
25
+ onReady?: ((details: readonly FilePreviewDetail[]) => any) | undefined;
26
+ onPrevious?: (() => any) | undefined;
27
+ onDownload?: (() => any) | undefined;
28
+ onNext?: (() => any) | undefined;
29
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
33
+ type __VLS_WithSlots<T, S> = T & {
34
+ new (): {
35
+ $slots: S;
36
+ };
37
+ };
@@ -0,0 +1,173 @@
1
+ <script setup>
2
+ import UButton from "@nuxt/ui/components/Button.vue";
3
+ import UIcon from "@nuxt/ui/components/Icon.vue";
4
+ import { useTimeoutFn } from "@vueuse/core";
5
+ import { computed, ref, shallowRef, watch } from "vue";
6
+ import { useUiToolsLocale } from "../../../i18n/use-locale";
7
+ import { filePreviewRendererComponent } from "../../utils/renderers";
8
+ import FilePreviewMessage from "./file-preview-message.vue";
9
+ const props = defineProps({
10
+ item: { type: Object, required: true },
11
+ display: { type: Object, required: true },
12
+ renderer: { type: Object, required: true },
13
+ source: { type: null, required: true },
14
+ container: { type: String, required: true },
15
+ kindLabel: { type: String, required: true },
16
+ arrows: { type: [Object, null], required: true }
17
+ });
18
+ const emit = defineEmits(["ready", "previous", "next", "download"]);
19
+ const { t } = useUiToolsLocale();
20
+ const failure = shallowRef(null);
21
+ const ready = ref(false);
22
+ const slow = ref(false);
23
+ const attempt = ref(0);
24
+ const delay = useTimeoutFn(() => slow.value = true, 150, { immediate: false });
25
+ const component = computed(() => filePreviewRendererComponent(props.renderer));
26
+ const converted = computed(() => props.display !== props.item);
27
+ const downloadable = computed(() => props.item.file.download !== false);
28
+ const extension = computed(() => (props.display.extension ?? props.kindLabel).toUpperCase());
29
+ function restart() {
30
+ failure.value = null;
31
+ ready.value = false;
32
+ slow.value = false;
33
+ delay.start();
34
+ }
35
+ watch(() => [props.display.key, props.source.url.value], restart, { immediate: true });
36
+ function handleReady(details) {
37
+ ready.value = true;
38
+ delay.stop();
39
+ emit("ready", details ?? []);
40
+ }
41
+ function handleError(error) {
42
+ failure.value = error;
43
+ delay.stop();
44
+ }
45
+ function retry() {
46
+ attempt.value += 1;
47
+ restart();
48
+ if (props.source.retryable.value) void props.source.retry();
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <div class="flex min-h-0 flex-1 flex-col" data-file-preview-stage="" :data-kind="display.kind">
54
+ <div
55
+ v-if="converted"
56
+ class="flex shrink-0 flex-wrap items-center gap-x-2.5 gap-y-1 border-b border-default bg-elevated px-4 py-2 text-[12.5px] text-toned"
57
+ data-file-preview-converted=""
58
+ >
59
+ <UIcon name="i-lucide-repeat" class="size-3.5 text-muted" aria-hidden="true" />
60
+ <span>
61
+ <b class="font-semibold text-highlighted">{{ t("filePreview.converted.title") }}.</b>
62
+ {{ t("filePreview.converted.description", { kind: kindLabel }) }}
63
+ </span>
64
+ <UButton
65
+ v-if="downloadable"
66
+ :label="t('filePreview.converted.download')"
67
+ color="neutral"
68
+ variant="link"
69
+ size="xs"
70
+ class="ms-auto p-0 font-semibold"
71
+ @click="emit('download')"
72
+ />
73
+ </div>
74
+
75
+ <div class="relative min-h-0 flex-1 overflow-hidden bg-(--nut-fp-stage)">
76
+ <div
77
+ v-if="source.status.value === 'loading'"
78
+ class="grid size-full place-items-center p-8"
79
+ data-file-preview-loading=""
80
+ >
81
+ <span
82
+ class="h-2/3 w-2/3 max-w-2xl animate-pulse rounded-md bg-accented"
83
+ aria-hidden="true"
84
+ />
85
+ </div>
86
+
87
+ <FilePreviewMessage
88
+ v-else-if="source.status.value === 'error'"
89
+ icon="i-lucide-triangle-alert"
90
+ tone="error"
91
+ :title="t('filePreview.errors.sourceTitle')"
92
+ :description="t('filePreview.errors.sourceDescription')"
93
+ :detail="source.error.value?.message"
94
+ >
95
+ <UButton
96
+ v-if="source.retryable.value"
97
+ icon="i-lucide-refresh-cw"
98
+ :label="t('filePreview.retry')"
99
+ data-file-preview-retry=""
100
+ @click="retry"
101
+ />
102
+ <UButton
103
+ v-if="downloadable"
104
+ icon="i-lucide-download"
105
+ :label="t('filePreview.download')"
106
+ color="neutral"
107
+ variant="outline"
108
+ @click="emit('download')"
109
+ />
110
+ </FilePreviewMessage>
111
+
112
+ <FilePreviewMessage
113
+ v-else-if="failure"
114
+ :icon="renderer.icon"
115
+ :badge="display.extension?.toUpperCase()"
116
+ tone="error"
117
+ :title="t('filePreview.errors.decodeTitle', { extension })"
118
+ :description="t('filePreview.errors.decodeDescription')"
119
+ :detail="failure.message"
120
+ >
121
+ <UButton
122
+ v-if="downloadable"
123
+ icon="i-lucide-download"
124
+ :label="t('filePreview.download')"
125
+ @click="emit('download')"
126
+ />
127
+ </FilePreviewMessage>
128
+
129
+ <component
130
+ :is="component"
131
+ v-else-if="source.url.value"
132
+ :key="`${display.key}:${attempt}`"
133
+ :file="display"
134
+ :url="source.url.value"
135
+ :blob="source.blob.value"
136
+ :container="container"
137
+ @ready="handleReady"
138
+ @error="handleError"
139
+ />
140
+
141
+ <div
142
+ v-if="slow && !ready && !failure && source.status.value !== 'error'"
143
+ class="pointer-events-none absolute inset-x-0 top-0 z-10 h-0.5 overflow-hidden"
144
+ aria-hidden="true"
145
+ >
146
+ <span class="nut-fp-progress block h-full w-1/3 bg-primary" />
147
+ </div>
148
+
149
+ <template v-if="arrows">
150
+ <button
151
+ v-if="arrows.previous"
152
+ type="button"
153
+ class="absolute top-1/2 left-3.5 z-10 grid size-10 -translate-y-1/2 place-items-center rounded-full border border-default bg-default/90 text-highlighted shadow-md backdrop-blur-sm transition hover:bg-default"
154
+ :aria-label="t('filePreview.previous')"
155
+ @click="emit('previous')"
156
+ >
157
+ <UIcon name="i-lucide-chevron-left" class="size-5" />
158
+ </button>
159
+ <button
160
+ v-if="arrows.next"
161
+ type="button"
162
+ class="absolute top-1/2 right-3.5 z-10 grid size-10 -translate-y-1/2 place-items-center rounded-full border border-default bg-default/90 text-highlighted shadow-md backdrop-blur-sm transition hover:bg-default"
163
+ :aria-label="t('filePreview.next')"
164
+ @click="emit('next')"
165
+ >
166
+ <UIcon name="i-lucide-chevron-right" class="size-5" />
167
+ </button>
168
+ </template>
169
+
170
+ <slot />
171
+ </div>
172
+ </div>
173
+ </template>
@@ -0,0 +1,37 @@
1
+ import type { useFilePreviewSource } from '../../composables/use-file-preview-source.js';
2
+ import type { FilePreviewContainer, FilePreviewDetail, FilePreviewItem, FilePreviewRendererDefinition } from '../../types/index.js';
3
+ type __VLS_Props = {
4
+ item: FilePreviewItem;
5
+ display: FilePreviewItem;
6
+ renderer: FilePreviewRendererDefinition;
7
+ source: ReturnType<typeof useFilePreviewSource>;
8
+ container: FilePreviewContainer;
9
+ kindLabel: string;
10
+ arrows: {
11
+ previous: boolean;
12
+ next: boolean;
13
+ } | null;
14
+ };
15
+ declare var __VLS_64: {};
16
+ type __VLS_Slots = {} & {
17
+ default?: (props: typeof __VLS_64) => any;
18
+ };
19
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
+ ready: (details: readonly FilePreviewDetail[]) => any;
21
+ previous: () => any;
22
+ download: () => any;
23
+ next: () => any;
24
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
25
+ onReady?: ((details: readonly FilePreviewDetail[]) => any) | undefined;
26
+ onPrevious?: (() => any) | undefined;
27
+ onDownload?: (() => any) | undefined;
28
+ onNext?: (() => any) | undefined;
29
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
33
+ type __VLS_WithSlots<T, S> = T & {
34
+ new (): {
35
+ $slots: S;
36
+ };
37
+ };
@@ -0,0 +1,14 @@
1
+ import type { FilePreviewItem } from '../../types/index.js';
2
+ type __VLS_Props = {
3
+ item: FilePreviewItem;
4
+ icon: string;
5
+ current: boolean;
6
+ label: string;
7
+ };
8
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
9
+ select: () => any;
10
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ onSelect?: (() => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;