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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +18 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.d.vue.ts +7 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.vue +147 -0
- package/dist/runtime/file-preview/components/provider/file-preview-host.vue.d.ts +7 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.d.vue.ts +21 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.vue +23 -0
- package/dist/runtime/file-preview/components/provider/file-preview-provider.vue.d.ts +21 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +83 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.vue +116 -0
- package/dist/runtime/file-preview/components/renderers/csv-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue +41 -0
- package/dist/runtime/file-preview/components/renderers/fallback-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.d.vue.ts +9 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.vue +83 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-code.vue.d.ts +9 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.d.vue.ts +3 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue +14 -0
- package/dist/runtime/file-preview/components/renderers/file-preview-text-loading.vue.d.ts +3 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.vue +182 -0
- package/dist/runtime/file-preview/components/renderers/image-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue +78 -0
- package/dist/runtime/file-preview/components/renderers/markdown-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.vue +145 -0
- package/dist/runtime/file-preview/components/renderers/media-controls.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue +58 -0
- package/dist/runtime/file-preview/components/renderers/pdf-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.vue +41 -0
- package/dist/runtime/file-preview/components/renderers/text-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.d.vue.ts +10 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue +110 -0
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue.d.ts +10 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.d.vue.ts +16 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.vue +40 -0
- package/dist/runtime/file-preview/components/shell/file-preview-button.vue.d.ts +16 -0
- package/dist/runtime/{form/components/renderer/form-field-error.d.vue.ts → file-preview/components/shell/file-preview-details.d.vue.ts} +4 -2
- package/dist/runtime/file-preview/components/shell/file-preview-details.vue +25 -0
- package/dist/runtime/{form/components/renderer/form-field-error.vue.d.ts → file-preview/components/shell/file-preview-details.vue.d.ts} +4 -2
- package/dist/runtime/file-preview/components/shell/file-preview-header.d.vue.ts +44 -0
- package/dist/runtime/file-preview/components/shell/file-preview-header.vue +163 -0
- package/dist/runtime/file-preview/components/shell/file-preview-header.vue.d.ts +44 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.d.vue.ts +21 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.vue +49 -0
- package/dist/runtime/file-preview/components/shell/file-preview-message.vue.d.ts +21 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.d.vue.ts +17 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.vue +280 -0
- package/dist/runtime/file-preview/components/shell/file-preview-shell.vue.d.ts +17 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.d.vue.ts +37 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.vue +173 -0
- package/dist/runtime/file-preview/components/shell/file-preview-stage.vue.d.ts +37 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.d.vue.ts +14 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue +69 -0
- package/dist/runtime/file-preview/components/shell/file-preview-thumb.vue.d.ts +14 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.d.vue.ts +13 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.vue +10 -0
- package/dist/runtime/file-preview/components/shell/file-preview-tools.vue.d.ts +13 -0
- package/dist/runtime/file-preview/composables/use-file-preview-api.d.ts +21 -0
- package/dist/runtime/file-preview/composables/use-file-preview-api.js +83 -0
- package/dist/runtime/file-preview/composables/use-file-preview-shell.d.ts +13 -0
- package/dist/runtime/file-preview/composables/use-file-preview-shell.js +30 -0
- package/dist/runtime/file-preview/composables/use-file-preview-source.d.ts +17 -0
- package/dist/runtime/file-preview/composables/use-file-preview-source.js +51 -0
- package/dist/runtime/file-preview/composables/use-file-preview-text.d.ts +14 -0
- package/dist/runtime/file-preview/composables/use-file-preview-text.js +79 -0
- package/dist/runtime/file-preview/index.d.ts +5 -0
- package/dist/runtime/file-preview/index.js +14 -0
- package/dist/runtime/file-preview/plugins/file-preview-api.d.ts +11 -0
- package/dist/runtime/file-preview/plugins/file-preview-api.js +10 -0
- package/dist/runtime/file-preview/types/api.d.ts +74 -0
- package/dist/runtime/file-preview/types/api.js +0 -0
- package/dist/runtime/file-preview/types/file.d.ts +77 -0
- package/dist/runtime/file-preview/types/file.js +0 -0
- package/dist/runtime/file-preview/types/index.d.ts +4 -0
- package/dist/runtime/file-preview/types/index.js +0 -0
- package/dist/runtime/file-preview/types/renderer.d.ts +49 -0
- package/dist/runtime/file-preview/types/renderer.js +0 -0
- package/dist/runtime/file-preview/types/shell.d.ts +12 -0
- package/dist/runtime/file-preview/types/shell.js +0 -0
- package/dist/runtime/file-preview/utils/container.d.ts +4 -0
- package/dist/runtime/file-preview/utils/container.js +10 -0
- package/dist/runtime/file-preview/utils/csv.d.ts +17 -0
- package/dist/runtime/file-preview/utils/csv.js +53 -0
- package/dist/runtime/file-preview/utils/download.d.ts +8 -0
- package/dist/runtime/file-preview/utils/download.js +40 -0
- package/dist/runtime/file-preview/utils/files.d.ts +7 -0
- package/dist/runtime/file-preview/utils/files.js +53 -0
- package/dist/runtime/file-preview/utils/format.d.ts +13 -0
- package/dist/runtime/file-preview/utils/format.js +40 -0
- package/dist/runtime/file-preview/utils/instance.d.ts +9 -0
- package/dist/runtime/file-preview/utils/instance.js +57 -0
- package/dist/runtime/file-preview/utils/renderers.d.ts +36 -0
- package/dist/runtime/file-preview/utils/renderers.js +247 -0
- package/dist/runtime/form/components/page/form-page-header.d.vue.ts +3 -1
- package/dist/runtime/form/components/page/form-page-header.vue +4 -4
- package/dist/runtime/form/components/page/form-page-header.vue.d.ts +3 -1
- package/dist/runtime/form/components/page/form-page-navigation.vue +2 -1
- package/dist/runtime/form/components/page/form-page-section.vue +15 -7
- package/dist/runtime/form/components/page/form-page.vue +0 -1
- package/dist/runtime/form/components/utils/form-file-preview.vue +5 -38
- package/dist/runtime/form/composables/use-field-control.d.ts +1 -1
- package/dist/runtime/form/composables/use-form-page.js +3 -1
- package/dist/runtime/form/composables/use-form-runtime.js +3 -0
- package/dist/runtime/form/composables/use-form-upload-registry.d.ts +2 -0
- package/dist/runtime/form/composables/use-form-upload-registry.js +11 -6
- package/dist/runtime/form/composables/use-form-validation.js +5 -1
- package/dist/runtime/form/fields/array-list/use-array-items.d.ts +1 -1
- package/dist/runtime/form/fields/array-table/array-table-cell.d.vue.ts +15 -0
- package/dist/runtime/form/fields/array-table/array-table-cell.vue +58 -0
- package/dist/runtime/form/fields/array-table/array-table-cell.vue.d.ts +15 -0
- package/dist/runtime/form/fields/array-table/component.vue +42 -26
- package/dist/runtime/form/fields/date/types.d.ts +6 -1
- package/dist/runtime/form/fields/date-family/component.vue +7 -1
- package/dist/runtime/form/fields/date-family/utils.d.ts +8 -1
- package/dist/runtime/form/fields/date-family/utils.js +10 -2
- package/dist/runtime/form/fields/hidden/types.d.ts +8 -1
- package/dist/runtime/form/fields/upload/component.vue +55 -176
- package/dist/runtime/form/fields/upload/types.d.ts +68 -3
- package/dist/runtime/form/fields/upload/upload-item.d.vue.ts +24 -0
- package/dist/runtime/form/fields/upload/upload-item.vue +158 -0
- package/dist/runtime/form/fields/upload/upload-item.vue.d.ts +24 -0
- package/dist/runtime/form/fields/upload/use-upload-items.d.ts +44 -0
- package/dist/runtime/form/fields/upload/use-upload-items.js +317 -0
- package/dist/runtime/form/types/callbacks.d.ts +8 -2
- package/dist/runtime/form/types/controller.d.ts +6 -3
- package/dist/runtime/form/types/field-base.d.ts +5 -5
- package/dist/runtime/form/types/field-output-utils.d.ts +12 -1
- package/dist/runtime/form/types/field-output.d.ts +1 -1
- package/dist/runtime/form/types/field.d.ts +3 -2
- package/dist/runtime/form/types/form-api.d.ts +4 -2
- package/dist/runtime/form/types/options-runtime.d.ts +8 -4
- package/dist/runtime/form/types/options.d.ts +11 -11
- package/dist/runtime/form/types/output.d.ts +33 -10
- package/dist/runtime/form/types/page.d.ts +7 -3
- package/dist/runtime/form/types/transform.d.ts +5 -2
- package/dist/runtime/form/types/ui.d.ts +7 -3
- package/dist/runtime/form/types/upload-runtime.d.ts +4 -0
- package/dist/runtime/form/utils/page.js +24 -5
- package/dist/runtime/form/utils/state.js +14 -11
- package/dist/runtime/i18n/locales/en.js +104 -1
- package/dist/runtime/i18n/locales/fr.js +104 -1
- package/dist/runtime/i18n/types.d.ts +121 -0
- package/dist/runtime/query-state/composables.d.ts +2 -2
- package/dist/runtime/shared/styles/tokens.css +1 -1
- package/dist/runtime/shared/types/utils.d.ts +12 -4
- package/dist/runtime/shared/utils/file.d.ts +11 -0
- package/dist/runtime/shared/utils/file.js +50 -0
- package/dist/runtime/table/components/table/table-renderer.vue +23 -3
- package/dist/runtime/table/components/table/table-skeleton-cell.js +2 -2
- package/dist/runtime/table/components/utils/search-query-input.d.vue.ts +5 -1
- package/dist/runtime/table/components/utils/search-query-input.vue +17 -3
- package/dist/runtime/table/components/utils/search-query-input.vue.d.ts +5 -1
- package/package.json +56 -29
- package/dist/runtime/form/components/renderer/form-field-error.vue +0 -16
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
|
+
import { useObjectUrl } from "@vueuse/core";
|
|
4
|
+
import { computed, ref } from "vue";
|
|
5
|
+
import { isString } from "../../../shared/utils/predicate";
|
|
6
|
+
const INLINE_THUMBNAIL_BYTES = 1.5 * 1024 * 1024;
|
|
7
|
+
const props = defineProps({
|
|
8
|
+
item: { type: Object, required: true },
|
|
9
|
+
icon: { type: String, required: true },
|
|
10
|
+
current: { type: Boolean, required: true },
|
|
11
|
+
label: { type: String, required: true }
|
|
12
|
+
});
|
|
13
|
+
defineEmits(["select"]);
|
|
14
|
+
const broken = ref(false);
|
|
15
|
+
const display = computed(() => props.item.rendition ?? props.item);
|
|
16
|
+
const localImage = computed(() => {
|
|
17
|
+
const { src } = display.value.file;
|
|
18
|
+
return display.value.kind === "image" && src instanceof Blob ? src : null;
|
|
19
|
+
});
|
|
20
|
+
const localUrl = useObjectUrl(localImage);
|
|
21
|
+
const image = computed(() => {
|
|
22
|
+
const { file, kind, size } = display.value;
|
|
23
|
+
if (props.item.file.thumbnail) return props.item.file.thumbnail;
|
|
24
|
+
if (file.poster) return file.poster;
|
|
25
|
+
if (localUrl.value) return localUrl.value;
|
|
26
|
+
const small = size === null || size <= INLINE_THUMBNAIL_BYTES;
|
|
27
|
+
return kind === "image" && small && isString(file.src) ? file.src : null;
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
:class="[
|
|
35
|
+
'relative grid h-11 w-16 shrink-0 place-items-center overflow-hidden rounded-md border bg-elevated text-muted transition',
|
|
36
|
+
current ? 'border-transparent outline-2 outline-offset-1 outline-primary' : 'border-default opacity-80 hover:opacity-100'
|
|
37
|
+
]"
|
|
38
|
+
:aria-label="label"
|
|
39
|
+
:aria-current="current ? 'true' : void 0"
|
|
40
|
+
data-file-preview-thumb=""
|
|
41
|
+
@click="$emit('select')"
|
|
42
|
+
>
|
|
43
|
+
<img
|
|
44
|
+
v-if="image && !broken"
|
|
45
|
+
:src="image"
|
|
46
|
+
alt=""
|
|
47
|
+
loading="lazy"
|
|
48
|
+
class="size-full object-cover"
|
|
49
|
+
@error="broken = true"
|
|
50
|
+
/>
|
|
51
|
+
<template v-else>
|
|
52
|
+
<UIcon :name="icon" class="size-4" aria-hidden="true" />
|
|
53
|
+
<span
|
|
54
|
+
v-if="item.extension"
|
|
55
|
+
class="absolute right-1 bottom-0.5 font-mono text-[8.5px] leading-none font-semibold uppercase"
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
>
|
|
58
|
+
{{ item.extension }}
|
|
59
|
+
</span>
|
|
60
|
+
</template>
|
|
61
|
+
<span
|
|
62
|
+
v-if="display.kind === 'video'"
|
|
63
|
+
class="absolute inset-0 grid place-items-center bg-black/25 text-white"
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
>
|
|
66
|
+
<UIcon name="i-lucide-play" class="size-3.5" />
|
|
67
|
+
</span>
|
|
68
|
+
</button>
|
|
69
|
+
</template>
|
|
@@ -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;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare var __VLS_7: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_7) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
9
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
10
|
+
new (): {
|
|
11
|
+
$slots: S;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useFilePreviewShell } from "../../composables/use-file-preview-shell";
|
|
3
|
+
const shell = useFilePreviewShell();
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<Teleport v-if="shell.tools.value" :to="shell.tools.value">
|
|
8
|
+
<slot />
|
|
9
|
+
</Teleport>
|
|
10
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare var __VLS_7: {};
|
|
2
|
+
type __VLS_Slots = {} & {
|
|
3
|
+
default?: (props: typeof __VLS_7) => any;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
9
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
10
|
+
new (): {
|
|
11
|
+
$slots: S;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue';
|
|
2
|
+
import type { FilePreviewApi } from '../types/index.js';
|
|
3
|
+
export declare const filePreviewApiKey: InjectionKey<FilePreviewApi>;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a file preview API. Inside a Nuxt app the module already creates one for the whole app,
|
|
6
|
+
* exposed as `useNuxtApp().$filePreview` and `useFilePreview()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createFilePreviewApi(): FilePreviewApi;
|
|
9
|
+
/**
|
|
10
|
+
* Provides the file preview API to a subtree. Inside a Nuxt app it reuses the app-wide instance,
|
|
11
|
+
* so previews opened from actions outside setup render in this provider.
|
|
12
|
+
*/
|
|
13
|
+
export declare function provideFilePreview(): FilePreviewApi;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the file preview API.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const filePreview = useFilePreview()
|
|
19
|
+
* filePreview.open(files, { index: 2 })
|
|
20
|
+
*/
|
|
21
|
+
export declare function useFilePreview(): FilePreviewApi;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { computed, inject, provide, ref, shallowRef } from "vue";
|
|
2
|
+
import { createFilePreviewInstance } from "../utils/instance.js";
|
|
3
|
+
import { mergeFilePreviewRenderers } from "../utils/renderers.js";
|
|
4
|
+
export const filePreviewApiKey = Symbol(
|
|
5
|
+
"nuxt-ui-tools-file-preview-api"
|
|
6
|
+
);
|
|
7
|
+
export function createFilePreviewApi() {
|
|
8
|
+
const instances = shallowRef([]);
|
|
9
|
+
const custom = shallowRef([]);
|
|
10
|
+
const providers = ref(0);
|
|
11
|
+
const renderers = computed(() => mergeFilePreviewRenderers(custom.value));
|
|
12
|
+
let sequence = 0;
|
|
13
|
+
function find(id) {
|
|
14
|
+
return instances.value.find((instance) => instance.id === id) ?? null;
|
|
15
|
+
}
|
|
16
|
+
function remove(id) {
|
|
17
|
+
instances.value = instances.value.filter((instance) => instance.id !== id);
|
|
18
|
+
}
|
|
19
|
+
const open = (input, options = {}) => {
|
|
20
|
+
const existing = options.id ? find(options.id) : null;
|
|
21
|
+
if (existing) {
|
|
22
|
+
existing.handle.update(input);
|
|
23
|
+
if (options.index !== void 0) existing.handle.goTo(options.index);
|
|
24
|
+
existing.open.value = true;
|
|
25
|
+
return existing.handle;
|
|
26
|
+
}
|
|
27
|
+
sequence += 1;
|
|
28
|
+
const instance = createFilePreviewInstance({
|
|
29
|
+
id: options.id ?? `file-preview-${sequence}`,
|
|
30
|
+
input,
|
|
31
|
+
onDispose: remove,
|
|
32
|
+
options,
|
|
33
|
+
renderers: () => renderers.value
|
|
34
|
+
});
|
|
35
|
+
instances.value = [...instances.value, instance];
|
|
36
|
+
return instance.handle;
|
|
37
|
+
};
|
|
38
|
+
function close(id) {
|
|
39
|
+
const instance = id ? find(id) : instances.value.at(-1) ?? null;
|
|
40
|
+
if (!instance) return false;
|
|
41
|
+
instance.handle.close();
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
function register(renderer) {
|
|
45
|
+
custom.value = [renderer, ...custom.value.filter((entry) => entry.kind !== renderer.kind)];
|
|
46
|
+
return () => {
|
|
47
|
+
custom.value = custom.value.filter((entry) => entry !== renderer);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function attach() {
|
|
51
|
+
providers.value += 1;
|
|
52
|
+
let attached = true;
|
|
53
|
+
return () => {
|
|
54
|
+
if (!attached) return;
|
|
55
|
+
attached = false;
|
|
56
|
+
providers.value -= 1;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
attach,
|
|
61
|
+
close,
|
|
62
|
+
closeAll: () => {
|
|
63
|
+
for (const instance of instances.value) instance.handle.close();
|
|
64
|
+
},
|
|
65
|
+
instances: computed(() => instances.value),
|
|
66
|
+
isOpen: (id) => id ? find(id) !== null : instances.value.length > 0,
|
|
67
|
+
mounted: computed(() => providers.value > 0),
|
|
68
|
+
open,
|
|
69
|
+
register,
|
|
70
|
+
renderers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function provideFilePreview() {
|
|
74
|
+
const api = inject(filePreviewApiKey, null) ?? createFilePreviewApi();
|
|
75
|
+
provide(filePreviewApiKey, api);
|
|
76
|
+
return api;
|
|
77
|
+
}
|
|
78
|
+
export function useFilePreview() {
|
|
79
|
+
const api = inject(filePreviewApiKey, null);
|
|
80
|
+
if (!api)
|
|
81
|
+
throw new Error("File preview API is not provided. Wrap your app with <UiFilePreviewProvider>.");
|
|
82
|
+
return api;
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue';
|
|
2
|
+
import type { FilePreviewMarkdownRenderer, FilePreviewShellContext } from '../types/index.js';
|
|
3
|
+
export declare const filePreviewShellKey: InjectionKey<FilePreviewShellContext>;
|
|
4
|
+
export declare const filePreviewMarkdownKey: InjectionKey<FilePreviewMarkdownRenderer | null>;
|
|
5
|
+
/** Whether a key press belongs to the focused control rather than to the preview. */
|
|
6
|
+
export declare function isFilePreviewControlKey(event: KeyboardEvent): boolean;
|
|
7
|
+
/** Shell of the preview the calling renderer is shown in. */
|
|
8
|
+
export declare function useFilePreviewShell(): FilePreviewShellContext;
|
|
9
|
+
/**
|
|
10
|
+
* Runs `handler` for the given keys while the preview has focus, except when a field, slider, menu,
|
|
11
|
+
* or embedded document owns the key.
|
|
12
|
+
*/
|
|
13
|
+
export declare function onFilePreviewKey(keys: readonly string[], handler: (event: KeyboardEvent) => void): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useEventListener } from "@vueuse/core";
|
|
2
|
+
import { inject } from "vue";
|
|
3
|
+
export const filePreviewShellKey = Symbol(
|
|
4
|
+
"nuxt-ui-tools-file-preview-shell"
|
|
5
|
+
);
|
|
6
|
+
export const filePreviewMarkdownKey = Symbol(
|
|
7
|
+
"nuxt-ui-tools-file-preview-markdown"
|
|
8
|
+
);
|
|
9
|
+
const KEY_OWNERS = 'input, textarea, select, iframe, [contenteditable="true"], [role="slider"], [role="menu"], [role="menuitem"]';
|
|
10
|
+
export function isFilePreviewControlKey(event) {
|
|
11
|
+
if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return true;
|
|
12
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
13
|
+
if (!target) return false;
|
|
14
|
+
if (target.closest(KEY_OWNERS)) return true;
|
|
15
|
+
return (event.key === " " || event.key === "Enter") && target.closest("button, a") !== null;
|
|
16
|
+
}
|
|
17
|
+
export function useFilePreviewShell() {
|
|
18
|
+
const shell = inject(filePreviewShellKey, null);
|
|
19
|
+
if (!shell)
|
|
20
|
+
throw new Error("useFilePreviewShell() must be called inside a file preview renderer.");
|
|
21
|
+
return shell;
|
|
22
|
+
}
|
|
23
|
+
export function onFilePreviewKey(keys, handler) {
|
|
24
|
+
const shell = useFilePreviewShell();
|
|
25
|
+
useEventListener(shell.root, "keydown", (event) => {
|
|
26
|
+
if (!keys.includes(event.key) || isFilePreviewControlKey(event)) return;
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
handler(event);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FilePreviewItem } from '../types/index.js';
|
|
2
|
+
type FilePreviewSourceStatus = 'loading' | 'ready' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the shown file's source. URLs are used as they are, `Blob` sources become an object URL
|
|
5
|
+
* that is revoked when the file changes, and source functions run once per file for the life of
|
|
6
|
+
* the preview, then again on `retry()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useFilePreviewSource(item: () => FilePreviewItem | null): {
|
|
9
|
+
blob: import("vue").ComputedRef<Blob | null>;
|
|
10
|
+
error: import("vue").ShallowRef<Error | null, Error | null>;
|
|
11
|
+
/** Whether `retry()` can fetch the source again, which only a source function can. */
|
|
12
|
+
retryable: import("vue").ComputedRef<boolean>;
|
|
13
|
+
retry: () => Promise<void>;
|
|
14
|
+
status: import("vue").Ref<FilePreviewSourceStatus, FilePreviewSourceStatus>;
|
|
15
|
+
url: import("vue").ComputedRef<string | null>;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useObjectUrl } from "@vueuse/core";
|
|
2
|
+
import { computed, ref, shallowRef, watch } from "vue";
|
|
3
|
+
import { isFunction, isString } from "../../shared/utils/predicate.js";
|
|
4
|
+
const resolved = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
export function useFilePreviewSource(item) {
|
|
6
|
+
const value = shallowRef(null);
|
|
7
|
+
const status = ref("loading");
|
|
8
|
+
const error = shallowRef(null);
|
|
9
|
+
const blob = computed(() => value.value instanceof Blob ? value.value : null);
|
|
10
|
+
const objectUrl = useObjectUrl(blob);
|
|
11
|
+
const url = computed(() => isString(value.value) ? value.value : objectUrl.value ?? null);
|
|
12
|
+
let attempt = 0;
|
|
13
|
+
function settle(next) {
|
|
14
|
+
value.value = next;
|
|
15
|
+
error.value = null;
|
|
16
|
+
status.value = "ready";
|
|
17
|
+
}
|
|
18
|
+
async function resolve(options) {
|
|
19
|
+
attempt += 1;
|
|
20
|
+
const current = attempt;
|
|
21
|
+
const file = item();
|
|
22
|
+
if (!file) return;
|
|
23
|
+
const src = file.file.src;
|
|
24
|
+
if (isString(src) || src instanceof Blob) return settle(src);
|
|
25
|
+
const known = options.force ? void 0 : resolved.get(file);
|
|
26
|
+
if (known !== void 0) return settle(known);
|
|
27
|
+
value.value = null;
|
|
28
|
+
error.value = null;
|
|
29
|
+
status.value = "loading";
|
|
30
|
+
try {
|
|
31
|
+
const next = await src();
|
|
32
|
+
if (current !== attempt) return;
|
|
33
|
+
resolved.set(file, next);
|
|
34
|
+
settle(next);
|
|
35
|
+
} catch (cause) {
|
|
36
|
+
if (current !== attempt) return;
|
|
37
|
+
error.value = cause instanceof Error ? cause : new Error(String(cause));
|
|
38
|
+
status.value = "error";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
watch(item, () => resolve({ force: false }), { immediate: true });
|
|
42
|
+
return {
|
|
43
|
+
blob,
|
|
44
|
+
error,
|
|
45
|
+
/** Whether `retry()` can fetch the source again, which only a source function can. */
|
|
46
|
+
retryable: computed(() => isFunction(item()?.file.src)),
|
|
47
|
+
retry: () => resolve({ force: true }),
|
|
48
|
+
status,
|
|
49
|
+
url
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FilePreviewRendererProps } from '../types/index.js';
|
|
2
|
+
/** Text previews read at most this many bytes, then cancel the read. */
|
|
3
|
+
export declare const FILE_PREVIEW_TEXT_LIMIT: number;
|
|
4
|
+
type FilePreviewTextStatus = 'loading' | 'ready' | 'error';
|
|
5
|
+
/** Reads a text file for a renderer: from its bytes when it is a `Blob`, otherwise with `fetch`. */
|
|
6
|
+
export declare function useFilePreviewText(props: Pick<FilePreviewRendererProps, 'url' | 'blob'>): {
|
|
7
|
+
error: import("vue").ShallowRef<Error | null, Error | null>;
|
|
8
|
+
notice: import("vue").ComputedRef<string | null>;
|
|
9
|
+
status: import("vue").Ref<FilePreviewTextStatus, FilePreviewTextStatus>;
|
|
10
|
+
text: import("vue").Ref<string, string>;
|
|
11
|
+
total: import("vue").Ref<number | null, number | null>;
|
|
12
|
+
truncated: import("vue").Ref<boolean, boolean>;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { computed, onScopeDispose, ref, shallowRef, watch } from "vue";
|
|
2
|
+
import { useUiToolsLocale } from "../../i18n/use-locale.js";
|
|
3
|
+
import { formatFileSize } from "../../shared/utils/file.js";
|
|
4
|
+
export const FILE_PREVIEW_TEXT_LIMIT = 512 * 1024;
|
|
5
|
+
async function readBlob(blob) {
|
|
6
|
+
return {
|
|
7
|
+
text: await blob.slice(0, FILE_PREVIEW_TEXT_LIMIT).text(),
|
|
8
|
+
total: blob.size,
|
|
9
|
+
truncated: blob.size > FILE_PREVIEW_TEXT_LIMIT
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
async function readUrl(url, signal) {
|
|
13
|
+
const response = await fetch(url, { signal });
|
|
14
|
+
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`.trim());
|
|
15
|
+
const header = Number(response.headers.get("content-length"));
|
|
16
|
+
const total = Number.isFinite(header) && header > 0 ? header : null;
|
|
17
|
+
if (!response.body) {
|
|
18
|
+
const text2 = await response.text();
|
|
19
|
+
return {
|
|
20
|
+
text: text2.slice(0, FILE_PREVIEW_TEXT_LIMIT),
|
|
21
|
+
total,
|
|
22
|
+
truncated: text2.length > FILE_PREVIEW_TEXT_LIMIT
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const reader = response.body.getReader();
|
|
26
|
+
const decoder = new TextDecoder();
|
|
27
|
+
let text = "";
|
|
28
|
+
let received = 0;
|
|
29
|
+
for (; ; ) {
|
|
30
|
+
const chunk = await reader.read();
|
|
31
|
+
if (chunk.done) break;
|
|
32
|
+
const room = FILE_PREVIEW_TEXT_LIMIT - received;
|
|
33
|
+
received += chunk.value.byteLength;
|
|
34
|
+
if (chunk.value.byteLength > room) {
|
|
35
|
+
text += decoder.decode(chunk.value.subarray(0, room));
|
|
36
|
+
await reader.cancel();
|
|
37
|
+
return { text, total, truncated: true };
|
|
38
|
+
}
|
|
39
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
40
|
+
}
|
|
41
|
+
return { text: text + decoder.decode(), total: total ?? received, truncated: false };
|
|
42
|
+
}
|
|
43
|
+
export function useFilePreviewText(props) {
|
|
44
|
+
const { code, t } = useUiToolsLocale();
|
|
45
|
+
const text = ref("");
|
|
46
|
+
const truncated = ref(false);
|
|
47
|
+
const total = ref(null);
|
|
48
|
+
const status = ref("loading");
|
|
49
|
+
const error = shallowRef(null);
|
|
50
|
+
let controller = null;
|
|
51
|
+
async function load() {
|
|
52
|
+
controller?.abort();
|
|
53
|
+
const current = new AbortController();
|
|
54
|
+
controller = current;
|
|
55
|
+
status.value = "loading";
|
|
56
|
+
error.value = null;
|
|
57
|
+
try {
|
|
58
|
+
const result = props.blob ? await readBlob(props.blob) : await readUrl(props.url, current.signal);
|
|
59
|
+
if (current.signal.aborted) return;
|
|
60
|
+
text.value = result.text.replace(/^/u, "");
|
|
61
|
+
truncated.value = result.truncated;
|
|
62
|
+
total.value = result.total;
|
|
63
|
+
status.value = "ready";
|
|
64
|
+
} catch (cause) {
|
|
65
|
+
if (current.signal.aborted) return;
|
|
66
|
+
error.value = cause instanceof Error ? cause : new Error(String(cause));
|
|
67
|
+
status.value = "error";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
watch(() => [props.url, props.blob], load, { immediate: true });
|
|
71
|
+
onScopeDispose(() => controller?.abort());
|
|
72
|
+
const notice = computed(
|
|
73
|
+
() => truncated.value ? t("filePreview.truncated", {
|
|
74
|
+
size: formatFileSize(FILE_PREVIEW_TEXT_LIMIT, code.value),
|
|
75
|
+
total: total.value === null ? "\u2014" : formatFileSize(total.value, code.value)
|
|
76
|
+
}) : null
|
|
77
|
+
);
|
|
78
|
+
return { error, notice, status, text, total, truncated };
|
|
79
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createFilePreviewApi, filePreviewApiKey, provideFilePreview, useFilePreview, } from './composables/use-file-preview-api.js';
|
|
2
|
+
export { onFilePreviewKey, useFilePreviewShell } from './composables/use-file-preview-shell.js';
|
|
3
|
+
export { FILE_PREVIEW_TEXT_LIMIT, useFilePreviewText } from './composables/use-file-preview-text.js';
|
|
4
|
+
export { builtInFilePreviewRenderers, defineFilePreviewRenderer, detectFilePreviewKind, matchFilePreview, } from './utils/renderers.js';
|
|
5
|
+
export type * from './types';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createFilePreviewApi,
|
|
3
|
+
filePreviewApiKey,
|
|
4
|
+
provideFilePreview,
|
|
5
|
+
useFilePreview
|
|
6
|
+
} from "./composables/use-file-preview-api.js";
|
|
7
|
+
export { onFilePreviewKey, useFilePreviewShell } from "./composables/use-file-preview-shell.js";
|
|
8
|
+
export { FILE_PREVIEW_TEXT_LIMIT, useFilePreviewText } from "./composables/use-file-preview-text.js";
|
|
9
|
+
export {
|
|
10
|
+
builtInFilePreviewRenderers,
|
|
11
|
+
defineFilePreviewRenderer,
|
|
12
|
+
detectFilePreviewKind,
|
|
13
|
+
matchFilePreview
|
|
14
|
+
} from "./utils/renderers.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One file preview API for the whole app: components reach it with `useFilePreview()`, and code
|
|
3
|
+
* running outside setup (actions, stores) with `useNuxtApp().$filePreview`.
|
|
4
|
+
* `<UiFilePreviewProvider>` renders the previews it opens.
|
|
5
|
+
*/
|
|
6
|
+
declare const _default: import("#app/nuxt").Plugin<{
|
|
7
|
+
filePreview: import("..").FilePreviewApi;
|
|
8
|
+
}> & import("#app/nuxt").ObjectPlugin<{
|
|
9
|
+
filePreview: import("..").FilePreviewApi;
|
|
10
|
+
}>;
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from "#app";
|
|
2
|
+
import { createFilePreviewApi, filePreviewApiKey } from "../composables/use-file-preview-api.js";
|
|
3
|
+
export default defineNuxtPlugin({
|
|
4
|
+
name: "nuxt-ui-tools:file-preview-api",
|
|
5
|
+
setup(nuxtApp) {
|
|
6
|
+
const filePreview = createFilePreviewApi();
|
|
7
|
+
nuxtApp.vueApp.provide(filePreviewApiKey, filePreview);
|
|
8
|
+
return { provide: { filePreview } };
|
|
9
|
+
}
|
|
10
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ComputedRef, Ref, ShallowRef } from 'vue';
|
|
2
|
+
import type { MaybePromise } from '../../shared/types/utils.js';
|
|
3
|
+
import type { FilePreviewFile, FilePreviewInput, FilePreviewItem } from './file.js';
|
|
4
|
+
import type { FilePreviewModeInput, FilePreviewRendererDefinition } from './renderer.js';
|
|
5
|
+
export interface FilePreviewOptions {
|
|
6
|
+
/** Opening the same id again updates and keeps the existing preview instead of stacking one. */
|
|
7
|
+
id?: string;
|
|
8
|
+
/** File shown first in a gallery. */
|
|
9
|
+
index?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Container, or a responsive value such as `fullscreen md:drawer`. Without it, each file's
|
|
12
|
+
* renderer suggests one and the largest wins, so navigating a gallery never swaps containers.
|
|
13
|
+
*/
|
|
14
|
+
mode?: FilePreviewModeInput;
|
|
15
|
+
/** `strip` shows thumbnails, `counter` only the position. Phones always show the counter. */
|
|
16
|
+
gallery?: 'strip' | 'counter';
|
|
17
|
+
/** Wraps from the last file to the first. */
|
|
18
|
+
loop?: boolean;
|
|
19
|
+
/** Page PDFs open at, passed as the `#page=` open parameter. */
|
|
20
|
+
pdf?: {
|
|
21
|
+
page?: number;
|
|
22
|
+
};
|
|
23
|
+
onChange?: (file: FilePreviewFile, index: number) => void;
|
|
24
|
+
onClose?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export interface FilePreviewHandle {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly index: Readonly<Ref<number>>;
|
|
29
|
+
next: () => void;
|
|
30
|
+
previous: () => void;
|
|
31
|
+
goTo: (index: number) => void;
|
|
32
|
+
/** Replaces the files. The position is kept when it still exists. */
|
|
33
|
+
update: (input: FilePreviewInput | readonly FilePreviewInput[]) => void;
|
|
34
|
+
close: () => void;
|
|
35
|
+
/** Resolves once the preview has closed and left the screen. */
|
|
36
|
+
readonly closed: Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export interface FilePreviewInstance {
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly files: ShallowRef<readonly FilePreviewItem[]>;
|
|
41
|
+
readonly index: Ref<number>;
|
|
42
|
+
readonly open: Ref<boolean>;
|
|
43
|
+
readonly options: FilePreviewOptions;
|
|
44
|
+
readonly handle: FilePreviewHandle;
|
|
45
|
+
/** Called by the provider once the closing animation ends. */
|
|
46
|
+
dispose: () => void;
|
|
47
|
+
}
|
|
48
|
+
/** Turns markdown into sanitized HTML for the markdown renderer's preview view. */
|
|
49
|
+
export type FilePreviewMarkdownRenderer = (source: string) => MaybePromise<string>;
|
|
50
|
+
export interface FilePreviewApi {
|
|
51
|
+
/**
|
|
52
|
+
* Opens one file or a gallery and returns right away with controls.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const { $filePreview } = useNuxtApp()
|
|
56
|
+
* $filePreview.open({ src: document.url, name: document.fileName })
|
|
57
|
+
*/
|
|
58
|
+
open: (input: FilePreviewInput | readonly FilePreviewInput[], options?: FilePreviewOptions) => FilePreviewHandle;
|
|
59
|
+
/** Closes the preview with this id, or the top-most one. Returns whether one was open. */
|
|
60
|
+
close: (id?: string) => boolean;
|
|
61
|
+
closeAll: () => void;
|
|
62
|
+
isOpen: (id?: string) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Adds a renderer, checked before the built-in ones. Registering a built-in kind replaces it.
|
|
65
|
+
* Returns a function that removes it again.
|
|
66
|
+
*/
|
|
67
|
+
register: (renderer: FilePreviewRendererDefinition) => () => void;
|
|
68
|
+
readonly instances: ComputedRef<readonly FilePreviewInstance[]>;
|
|
69
|
+
readonly renderers: ComputedRef<readonly FilePreviewRendererDefinition[]>;
|
|
70
|
+
/** Whether a `<UiFilePreviewProvider>` is mounted to render previews. */
|
|
71
|
+
readonly mounted: ComputedRef<boolean>;
|
|
72
|
+
/** Called by the provider. Returns the function it calls when it unmounts. */
|
|
73
|
+
attach: () => () => void;
|
|
74
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { MaybePromise } from '../../shared/types/utils.js';
|
|
2
|
+
/** Text shown in the preview chrome. A getter keeps it translated when the language changes. */
|
|
3
|
+
export type FilePreviewText = string | (() => string);
|
|
4
|
+
/** Kinds the package renders out of the box. Custom renderers may add their own kind names. */
|
|
5
|
+
export type FilePreviewBuiltInKind = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'csv' | 'markdown' | 'office' | 'archive' | 'other';
|
|
6
|
+
export type FilePreviewKind = FilePreviewBuiltInKind | (string & {});
|
|
7
|
+
/** A usable source: a URL (remote, `data:`, or `blob:`) or the bytes themselves. */
|
|
8
|
+
export type FilePreviewSourceValue = string | Blob;
|
|
9
|
+
/**
|
|
10
|
+
* Where the file comes from. A function runs only when the file is shown, so short-lived signed
|
|
11
|
+
* URLs are requested on demand; it runs again when the user presses Retry after an error.
|
|
12
|
+
*/
|
|
13
|
+
export type FilePreviewSource = FilePreviewSourceValue | (() => MaybePromise<FilePreviewSourceValue>);
|
|
14
|
+
export interface FilePreviewDetail {
|
|
15
|
+
label: FilePreviewText;
|
|
16
|
+
value: FilePreviewText;
|
|
17
|
+
}
|
|
18
|
+
/** Extra header action, such as "Open in registry". */
|
|
19
|
+
export interface FilePreviewAction {
|
|
20
|
+
label: FilePreviewText;
|
|
21
|
+
icon?: string;
|
|
22
|
+
/** Link target. External URLs open in a new tab. */
|
|
23
|
+
to?: string;
|
|
24
|
+
onSelect?: (file: FilePreviewFile) => MaybePromise<void>;
|
|
25
|
+
}
|
|
26
|
+
/** Caption or subtitle track for video previews. */
|
|
27
|
+
export interface FilePreviewTrack {
|
|
28
|
+
src: string;
|
|
29
|
+
srcLang: string;
|
|
30
|
+
label: string;
|
|
31
|
+
kind?: TextTrackKind;
|
|
32
|
+
default?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface FilePreviewFile {
|
|
35
|
+
src: FilePreviewSource;
|
|
36
|
+
/** Stable identity inside a gallery. Defaults to the file position. */
|
|
37
|
+
id?: string;
|
|
38
|
+
/** Display name. Defaults to `File.name`, then the last URL segment. */
|
|
39
|
+
name?: string;
|
|
40
|
+
/** MIME type. Wins over extension sniffing to pick the renderer. */
|
|
41
|
+
mime?: string;
|
|
42
|
+
/** Forces a renderer, custom kinds included. */
|
|
43
|
+
kind?: FilePreviewKind;
|
|
44
|
+
size?: number;
|
|
45
|
+
updatedAt?: string | number | Date;
|
|
46
|
+
/** Small image for the gallery strip. */
|
|
47
|
+
thumbnail?: string;
|
|
48
|
+
/** Video poster or audio cover. */
|
|
49
|
+
poster?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Preview made by a server for a file browsers cannot render, such as a PDF for a DOCX or a JPEG
|
|
52
|
+
* for a HEIC photo. The preview shows the rendition and downloads keep the original.
|
|
53
|
+
*/
|
|
54
|
+
rendition?: FilePreviewFile;
|
|
55
|
+
details?: readonly FilePreviewDetail[];
|
|
56
|
+
/**
|
|
57
|
+
* `false` hides the download action, a string downloads that URL instead, and a function replaces
|
|
58
|
+
* the download entirely. By default the resolved source is downloaded under the file name.
|
|
59
|
+
*/
|
|
60
|
+
download?: boolean | string | ((file: FilePreviewFile) => MaybePromise<void>);
|
|
61
|
+
actions?: readonly FilePreviewAction[];
|
|
62
|
+
tracks?: readonly FilePreviewTrack[];
|
|
63
|
+
}
|
|
64
|
+
/** What `open()` accepts for each file: a URL, a `File` or `Blob`, or a full descriptor. */
|
|
65
|
+
export type FilePreviewInput = FilePreviewSourceValue | FilePreviewFile;
|
|
66
|
+
/** A file normalized once when the preview opens. Renderers receive it as `file`. */
|
|
67
|
+
export interface FilePreviewItem {
|
|
68
|
+
key: string;
|
|
69
|
+
file: FilePreviewFile;
|
|
70
|
+
name: string;
|
|
71
|
+
mime: string | null;
|
|
72
|
+
extension: string | null;
|
|
73
|
+
size: number | null;
|
|
74
|
+
updatedAt: Date | null;
|
|
75
|
+
kind: FilePreviewKind;
|
|
76
|
+
rendition: FilePreviewItem | null;
|
|
77
|
+
}
|
|
File without changes
|
|
File without changes
|