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,49 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
import type { FilePreviewDetail, FilePreviewItem, FilePreviewKind, FilePreviewText } from './file.js';
|
|
3
|
+
/** Overlay that hosts a preview. The same three containers forms use. */
|
|
4
|
+
export type FilePreviewContainer = 'modal' | 'drawer' | 'fullscreen';
|
|
5
|
+
/** A container, or a responsive value such as `fullscreen md:drawer`. */
|
|
6
|
+
export type FilePreviewModeInput = FilePreviewContainer | (string & {});
|
|
7
|
+
/** What detection sees of a file. */
|
|
8
|
+
export interface FilePreviewMatchContext {
|
|
9
|
+
name: string;
|
|
10
|
+
mime: string | null;
|
|
11
|
+
extension: string | null;
|
|
12
|
+
}
|
|
13
|
+
export type FilePreviewRendererLoader = () => Promise<Component | {
|
|
14
|
+
default: Component;
|
|
15
|
+
}>;
|
|
16
|
+
export interface FilePreviewRendererDefinition<TKind extends FilePreviewKind = FilePreviewKind> {
|
|
17
|
+
kind: TKind;
|
|
18
|
+
/** Icon of the kind tile, strip tile, and fallback glyph. */
|
|
19
|
+
icon: string;
|
|
20
|
+
/** Kind name shown in the meta line. Built-in kinds are translated by the package. */
|
|
21
|
+
label?: FilePreviewText;
|
|
22
|
+
/** Claims files for this renderer. Files that set `kind` skip matching. */
|
|
23
|
+
match?: (file: FilePreviewMatchContext) => boolean;
|
|
24
|
+
/** Loaded the first time a file of this kind is shown: `() => import('./EmailPreview.vue')`. */
|
|
25
|
+
component: FilePreviewRendererLoader;
|
|
26
|
+
/** Default container when `open()` has no `mode`. */
|
|
27
|
+
mode?: FilePreviewModeInput;
|
|
28
|
+
/** Uses a narrow modal for a single file, as audio and the fallback card do. */
|
|
29
|
+
compact?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type FilePreviewErrorReason = 'source' | 'decode' | 'unsupported';
|
|
32
|
+
export interface FilePreviewRendererError {
|
|
33
|
+
reason: FilePreviewErrorReason;
|
|
34
|
+
message?: string;
|
|
35
|
+
}
|
|
36
|
+
/** Props every renderer receives. */
|
|
37
|
+
export interface FilePreviewRendererProps {
|
|
38
|
+
file: FilePreviewItem;
|
|
39
|
+
/** Usable URL: the resolved URL, or an object URL for `Blob` sources. */
|
|
40
|
+
url: string;
|
|
41
|
+
/** Bytes when the source is a `Blob`, so text renderers can read them without a request. */
|
|
42
|
+
blob: Blob | null;
|
|
43
|
+
container: FilePreviewContainer;
|
|
44
|
+
}
|
|
45
|
+
export interface FilePreviewRendererEmits {
|
|
46
|
+
/** Content is on screen. Details such as dimensions or duration join the details panel. */
|
|
47
|
+
ready: [details?: readonly FilePreviewDetail[]];
|
|
48
|
+
error: [error: FilePreviewRendererError];
|
|
49
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue';
|
|
2
|
+
import type { FilePreviewMarkdownRenderer, FilePreviewOptions } from './api.js';
|
|
3
|
+
/** What the preview shell shares with the renderer it shows. */
|
|
4
|
+
export interface FilePreviewShellContext {
|
|
5
|
+
/** Dialog root. Renderers listen to its `keydown` for their own shortcuts. */
|
|
6
|
+
root: Readonly<Ref<HTMLElement | null>>;
|
|
7
|
+
/** Where `<UiFilePreviewTools>` puts renderer buttons: the header, or the bottom bar on phones. */
|
|
8
|
+
tools: Readonly<Ref<HTMLElement | null>>;
|
|
9
|
+
narrow: ComputedRef<boolean>;
|
|
10
|
+
options: FilePreviewOptions;
|
|
11
|
+
markdown: FilePreviewMarkdownRenderer | null;
|
|
12
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FilePreviewContainer } from '../types/index.js';
|
|
2
|
+
export declare function normalizeFilePreviewContainer(value: string | number | boolean | null | undefined): FilePreviewContainer;
|
|
3
|
+
/** The container a gallery keeps while navigating: the largest one any of its files needs. */
|
|
4
|
+
export declare function largestFilePreviewContainer(containers: readonly FilePreviewContainer[]): FilePreviewContainer;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const CONTAINER_RANK = { drawer: 1, fullscreen: 2, modal: 0 };
|
|
2
|
+
export function normalizeFilePreviewContainer(value) {
|
|
3
|
+
return value === "drawer" || value === "fullscreen" ? value : "modal";
|
|
4
|
+
}
|
|
5
|
+
export function largestFilePreviewContainer(containers) {
|
|
6
|
+
return containers.reduce(
|
|
7
|
+
(largest, container) => CONTAINER_RANK[container] > CONTAINER_RANK[largest] ? container : largest,
|
|
8
|
+
"modal"
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const DELIMITERS: readonly [",", ";", "\t", "|"];
|
|
2
|
+
type CsvDelimiter = (typeof DELIMITERS)[number];
|
|
3
|
+
/** Picks the delimiter that splits the first line into the most cells, outside quotes. */
|
|
4
|
+
export declare function detectCsvDelimiter(text: string): CsvDelimiter;
|
|
5
|
+
/**
|
|
6
|
+
* Parses RFC 4180 CSV: quoted cells, doubled quotes, and line breaks inside quotes. Stops after
|
|
7
|
+
* `maxRows` rows so a large export stays cheap to show.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseCsv(input: string, options?: {
|
|
10
|
+
delimiter?: string;
|
|
11
|
+
maxRows?: number;
|
|
12
|
+
}): {
|
|
13
|
+
complete: boolean;
|
|
14
|
+
delimiter: string;
|
|
15
|
+
rows: string[][];
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const DELIMITERS = [",", ";", " ", "|"];
|
|
2
|
+
export function detectCsvDelimiter(text) {
|
|
3
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4
|
+
let quoted = false;
|
|
5
|
+
for (const char of text) {
|
|
6
|
+
if (char === "\n") break;
|
|
7
|
+
if (char === '"') quoted = !quoted;
|
|
8
|
+
else if (!quoted) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
return DELIMITERS.reduce(
|
|
11
|
+
(best, delimiter) => (counts.get(delimiter) ?? 0) > (counts.get(best) ?? 0) ? delimiter : best
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
export function parseCsv(input, options = {}) {
|
|
15
|
+
const text = input.replace(/^/u, "");
|
|
16
|
+
const delimiter = options.delimiter ?? detectCsvDelimiter(text);
|
|
17
|
+
const maxRows = options.maxRows ?? Infinity;
|
|
18
|
+
const rows = [];
|
|
19
|
+
let row = [];
|
|
20
|
+
let cell = "";
|
|
21
|
+
let quoted = false;
|
|
22
|
+
let complete = true;
|
|
23
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
24
|
+
const char = text[index];
|
|
25
|
+
if (quoted) {
|
|
26
|
+
if (char !== '"') cell += char;
|
|
27
|
+
else if (text[index + 1] === '"') {
|
|
28
|
+
cell += '"';
|
|
29
|
+
index += 1;
|
|
30
|
+
} else quoted = false;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (char === '"') quoted = true;
|
|
34
|
+
else if (char === delimiter) {
|
|
35
|
+
row.push(cell);
|
|
36
|
+
cell = "";
|
|
37
|
+
} else if (char === "\n") {
|
|
38
|
+
row.push(cell);
|
|
39
|
+
rows.push(row);
|
|
40
|
+
row = [];
|
|
41
|
+
cell = "";
|
|
42
|
+
if (rows.length >= maxRows) {
|
|
43
|
+
complete = index >= text.length - 1;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
} else if (char !== "\r") cell += char;
|
|
47
|
+
}
|
|
48
|
+
if (complete && (cell !== "" || row.length > 0)) {
|
|
49
|
+
row.push(cell);
|
|
50
|
+
rows.push(row);
|
|
51
|
+
}
|
|
52
|
+
return { complete, delimiter, rows };
|
|
53
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FilePreviewItem } from '../types/index.js';
|
|
2
|
+
/** Downloads the original file, honoring its `download` option. */
|
|
3
|
+
export declare function downloadFilePreviewItem(item: FilePreviewItem): Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* Opens the original file in a new tab. The tab opens right away, while the click still counts as
|
|
6
|
+
* a user gesture, and receives its address once a source function resolves.
|
|
7
|
+
*/
|
|
8
|
+
export declare function openFilePreviewItem(item: FilePreviewItem): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isString } from "../../shared/utils/predicate.js";
|
|
2
|
+
async function sourceHref(src) {
|
|
3
|
+
const value = isString(src) || src instanceof Blob ? src : await src();
|
|
4
|
+
if (isString(value)) return { href: value, revoke: false };
|
|
5
|
+
return { href: URL.createObjectURL(value), revoke: true };
|
|
6
|
+
}
|
|
7
|
+
function click(params) {
|
|
8
|
+
const anchor = document.createElement("a");
|
|
9
|
+
anchor.href = params.href;
|
|
10
|
+
anchor.rel = "noopener";
|
|
11
|
+
if (params.download !== void 0) anchor.download = params.download;
|
|
12
|
+
if (!params.href.startsWith("blob:") && !params.href.startsWith("data:")) anchor.target = "_blank";
|
|
13
|
+
document.body.append(anchor);
|
|
14
|
+
anchor.click();
|
|
15
|
+
anchor.remove();
|
|
16
|
+
}
|
|
17
|
+
export async function downloadFilePreviewItem(item) {
|
|
18
|
+
const option = item.file.download;
|
|
19
|
+
if (option === false) return;
|
|
20
|
+
if (option !== true && option !== void 0 && !isString(option)) return await option(item.file);
|
|
21
|
+
const { href, revoke } = isString(option) ? { href: option, revoke: false } : await sourceHref(item.file.src);
|
|
22
|
+
click({ download: item.name, href });
|
|
23
|
+
if (revoke) setTimeout(() => URL.revokeObjectURL(href), 3e4);
|
|
24
|
+
}
|
|
25
|
+
export async function openFilePreviewItem(item) {
|
|
26
|
+
const { src } = item.file;
|
|
27
|
+
if (isString(src) || src instanceof Blob) {
|
|
28
|
+
const { href, revoke } = await sourceHref(src);
|
|
29
|
+
click({ href });
|
|
30
|
+
if (revoke) setTimeout(() => URL.revokeObjectURL(href), 3e4);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const tab = window.open("", "_blank");
|
|
34
|
+
try {
|
|
35
|
+
const { href } = await sourceHref(src);
|
|
36
|
+
if (tab) tab.location.href = href;
|
|
37
|
+
} catch {
|
|
38
|
+
tab?.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FilePreviewInput, FilePreviewItem, FilePreviewRendererDefinition } from '../types/index.js';
|
|
2
|
+
/** Normalizes one input: names, MIME type, size, and kind are settled once, when the preview opens. */
|
|
3
|
+
export declare function normalizeFilePreviewItem(input: FilePreviewInput, context: {
|
|
4
|
+
key: string;
|
|
5
|
+
renderers: readonly FilePreviewRendererDefinition[];
|
|
6
|
+
}): FilePreviewItem;
|
|
7
|
+
export declare function normalizeFilePreviewItems(input: FilePreviewInput | readonly FilePreviewInput[], renderers: readonly FilePreviewRendererDefinition[]): FilePreviewItem[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { fileExtension, fileNameFromUrl } from "../../shared/utils/file.js";
|
|
2
|
+
import { isString } from "../../shared/utils/predicate.js";
|
|
3
|
+
import { detectFilePreviewKind } from "./renderers.js";
|
|
4
|
+
function isBlob(value) {
|
|
5
|
+
return "Blob" in globalThis && value instanceof Blob;
|
|
6
|
+
}
|
|
7
|
+
function isNamedFile(value) {
|
|
8
|
+
return "File" in globalThis && value instanceof File;
|
|
9
|
+
}
|
|
10
|
+
function toFile(input) {
|
|
11
|
+
if (isString(input) || isBlob(input)) return { src: input };
|
|
12
|
+
return input;
|
|
13
|
+
}
|
|
14
|
+
function sourceName(src) {
|
|
15
|
+
if (isNamedFile(src)) return src.name;
|
|
16
|
+
if (isString(src) && !src.startsWith("data:") && !src.startsWith("blob:"))
|
|
17
|
+
return fileNameFromUrl(src);
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
function toDate(value, src) {
|
|
21
|
+
if (value !== void 0) {
|
|
22
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
23
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
24
|
+
}
|
|
25
|
+
return isNamedFile(src) ? new Date(src.lastModified) : null;
|
|
26
|
+
}
|
|
27
|
+
export function normalizeFilePreviewItem(input, context) {
|
|
28
|
+
const file = toFile(input);
|
|
29
|
+
const name = file.name ?? sourceName(file.src);
|
|
30
|
+
const blob = isBlob(file.src) ? file.src : null;
|
|
31
|
+
const mime = file.mime ?? (blob?.type || null);
|
|
32
|
+
const extension = fileExtension(name);
|
|
33
|
+
return {
|
|
34
|
+
extension,
|
|
35
|
+
file,
|
|
36
|
+
key: file.id ?? context.key,
|
|
37
|
+
kind: detectFilePreviewKind({ extension, kind: file.kind, mime, name }, context.renderers),
|
|
38
|
+
mime,
|
|
39
|
+
name,
|
|
40
|
+
rendition: file.rendition ? normalizeFilePreviewItem(file.rendition, { ...context, key: `${context.key}:rendition` }) : null,
|
|
41
|
+
size: file.size ?? blob?.size ?? null,
|
|
42
|
+
updatedAt: toDate(file.updatedAt, file.src)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function normalizeFilePreviewItems(input, renderers) {
|
|
46
|
+
const inputs = isInputList(input) ? input : [input];
|
|
47
|
+
return inputs.map(
|
|
48
|
+
(entry, index) => normalizeFilePreviewItem(entry, { key: `file-${index}`, renderers })
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
function isInputList(input) {
|
|
52
|
+
return Array.isArray(input);
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FilePreviewText } from '../types/index.js';
|
|
2
|
+
export declare function resolveFilePreviewText(text: FilePreviewText): FilePreviewText & string;
|
|
3
|
+
/** Media time as `m:ss`, or `h:mm:ss` past an hour. */
|
|
4
|
+
export declare function formatFilePreviewDuration(seconds: number): string;
|
|
5
|
+
export declare function formatFilePreviewDate(date: Date, locale: string): string;
|
|
6
|
+
/** Splits a name so the end stays visible when the start is truncated: `Contrat-cadre-…2026.pdf`. */
|
|
7
|
+
export declare function splitFilePreviewName(name: string): {
|
|
8
|
+
head: string;
|
|
9
|
+
tail: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function escapeFilePreviewHtml(text: string): string;
|
|
12
|
+
/** Escapes one line of formatted JSON and tints keys, strings, numbers, and literals. */
|
|
13
|
+
export declare function tintFilePreviewJsonLine(line: string): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isString } from "../../shared/utils/predicate.js";
|
|
2
|
+
export function resolveFilePreviewText(text) {
|
|
3
|
+
return isString(text) ? text : text();
|
|
4
|
+
}
|
|
5
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
6
|
+
export function formatFilePreviewDuration(seconds) {
|
|
7
|
+
const total = Number.isFinite(seconds) ? Math.max(0, Math.floor(seconds)) : 0;
|
|
8
|
+
const hours = Math.floor(total / 3600);
|
|
9
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
10
|
+
const rest = total % 60;
|
|
11
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(rest)}` : `${minutes}:${pad(rest)}`;
|
|
12
|
+
}
|
|
13
|
+
export function formatFilePreviewDate(date, locale) {
|
|
14
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: "medium" }).format(date);
|
|
15
|
+
}
|
|
16
|
+
export function splitFilePreviewName(name) {
|
|
17
|
+
const tail = Math.min(name.length, 10);
|
|
18
|
+
return { head: name.slice(0, name.length - tail), tail: name.slice(name.length - tail) };
|
|
19
|
+
}
|
|
20
|
+
const escapes = /* @__PURE__ */ new Map([
|
|
21
|
+
['"', """],
|
|
22
|
+
["&", "&"],
|
|
23
|
+
["'", "'"],
|
|
24
|
+
["<", "<"],
|
|
25
|
+
[">", ">"]
|
|
26
|
+
]);
|
|
27
|
+
export function escapeFilePreviewHtml(text) {
|
|
28
|
+
return text.replaceAll(/["&'<>]/gu, (char) => escapes.get(char) ?? char);
|
|
29
|
+
}
|
|
30
|
+
const JSON_TOKEN = /("(?:[^&]|&(?!quot;))*?")(\s*:)?|\b(true|false|null)\b|(-?\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)/gu;
|
|
31
|
+
export function tintFilePreviewJsonLine(line) {
|
|
32
|
+
return escapeFilePreviewHtml(line).replaceAll(
|
|
33
|
+
JSON_TOKEN,
|
|
34
|
+
(match, text, colon, literal) => {
|
|
35
|
+
if (text) return `<span data-token="${colon ? "key" : "string"}">${text}</span>${colon ?? ""}`;
|
|
36
|
+
if (literal) return `<span data-token="literal">${literal}</span>`;
|
|
37
|
+
return `<span data-token="number">${match}</span>`;
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FilePreviewInput, FilePreviewInstance, FilePreviewOptions, FilePreviewRendererDefinition } from '../types/index.js';
|
|
2
|
+
/** Creates the state of one open preview: its files, position, open flag, and public handle. */
|
|
3
|
+
export declare function createFilePreviewInstance(params: {
|
|
4
|
+
id: string;
|
|
5
|
+
input: FilePreviewInput | readonly FilePreviewInput[];
|
|
6
|
+
options: FilePreviewOptions;
|
|
7
|
+
renderers: () => readonly FilePreviewRendererDefinition[];
|
|
8
|
+
onDispose: (id: string) => void;
|
|
9
|
+
}): FilePreviewInstance;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readonly, ref, shallowRef } from "vue";
|
|
2
|
+
import { normalizeFilePreviewItems } from "./files.js";
|
|
3
|
+
function clamp(index, length) {
|
|
4
|
+
return Math.min(Math.max(0, Math.trunc(index)), Math.max(0, length - 1));
|
|
5
|
+
}
|
|
6
|
+
export function createFilePreviewInstance(params) {
|
|
7
|
+
const { id, options } = params;
|
|
8
|
+
const files = shallowRef(
|
|
9
|
+
normalizeFilePreviewItems(params.input, params.renderers())
|
|
10
|
+
);
|
|
11
|
+
const index = ref(clamp(options.index ?? 0, files.value.length));
|
|
12
|
+
const open = ref(true);
|
|
13
|
+
let settle = null;
|
|
14
|
+
let disposed = false;
|
|
15
|
+
const closed = new Promise((resolve) => {
|
|
16
|
+
settle = resolve;
|
|
17
|
+
});
|
|
18
|
+
function goTo(target) {
|
|
19
|
+
const count = files.value.length;
|
|
20
|
+
if (count === 0) return;
|
|
21
|
+
const next = options.loop ? (Math.trunc(target) % count + count) % count : clamp(target, count);
|
|
22
|
+
if (next === index.value) return;
|
|
23
|
+
index.value = next;
|
|
24
|
+
const item = files.value[next];
|
|
25
|
+
if (item) options.onChange?.(item.file, next);
|
|
26
|
+
}
|
|
27
|
+
const handle = {
|
|
28
|
+
close: () => {
|
|
29
|
+
open.value = false;
|
|
30
|
+
},
|
|
31
|
+
closed,
|
|
32
|
+
goTo,
|
|
33
|
+
id,
|
|
34
|
+
index: readonly(index),
|
|
35
|
+
next: () => goTo(index.value + 1),
|
|
36
|
+
previous: () => goTo(index.value - 1),
|
|
37
|
+
update: (input) => {
|
|
38
|
+
files.value = normalizeFilePreviewItems(input, params.renderers());
|
|
39
|
+
index.value = clamp(index.value, files.value.length);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
dispose: () => {
|
|
44
|
+
if (disposed) return;
|
|
45
|
+
disposed = true;
|
|
46
|
+
params.onDispose(id);
|
|
47
|
+
settle?.();
|
|
48
|
+
options.onClose?.();
|
|
49
|
+
},
|
|
50
|
+
files,
|
|
51
|
+
handle,
|
|
52
|
+
id,
|
|
53
|
+
index,
|
|
54
|
+
open,
|
|
55
|
+
options
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Component } from 'vue';
|
|
2
|
+
import type { UiToolsTranslator } from '../../i18n/utils.js';
|
|
3
|
+
import type { FilePreviewKind, FilePreviewMatchContext, FilePreviewRendererDefinition } from '../types/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Declares a renderer with its kind inferred as a literal.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* defineFilePreviewRenderer({
|
|
9
|
+
* kind: 'email',
|
|
10
|
+
* icon: 'i-lucide-mail',
|
|
11
|
+
* match: ({ mime, extension }) => mime === 'message/rfc822' || extension === 'eml',
|
|
12
|
+
* component: () => import('~/components/previews/EmailPreview.vue'),
|
|
13
|
+
* mode: 'fullscreen md:drawer',
|
|
14
|
+
* })
|
|
15
|
+
*/
|
|
16
|
+
export declare function defineFilePreviewRenderer<const TKind extends FilePreviewKind>(renderer: FilePreviewRendererDefinition<TKind>): FilePreviewRendererDefinition<TKind>;
|
|
17
|
+
/** Claims files by MIME type (a trailing `/` matches a whole family) or by extension. */
|
|
18
|
+
export declare function matchFilePreview(rule: {
|
|
19
|
+
mimes?: readonly string[];
|
|
20
|
+
extensions?: readonly string[];
|
|
21
|
+
}): (file: FilePreviewMatchContext) => boolean;
|
|
22
|
+
/** Renders anything no other renderer claims: a card with the file facts and a download. */
|
|
23
|
+
export declare const fallbackFilePreviewRenderer: FilePreviewRendererDefinition<"other">;
|
|
24
|
+
/** Checked in order, so specific text formats come before the generic text renderer. */
|
|
25
|
+
export declare const builtInFilePreviewRenderers: readonly FilePreviewRendererDefinition[];
|
|
26
|
+
/** Custom renderers first, then built-in ones that no custom renderer replaced. */
|
|
27
|
+
export declare function mergeFilePreviewRenderers(custom: readonly FilePreviewRendererDefinition[]): FilePreviewRendererDefinition<FilePreviewKind>[];
|
|
28
|
+
/** Kind of a file: the explicit one, then the first renderer that claims it, then `other`. */
|
|
29
|
+
export declare function detectFilePreviewKind(file: FilePreviewMatchContext & {
|
|
30
|
+
kind?: FilePreviewKind;
|
|
31
|
+
}, renderers: readonly FilePreviewRendererDefinition[]): FilePreviewKind;
|
|
32
|
+
export declare function findFilePreviewRenderer(kind: FilePreviewKind, renderers: readonly FilePreviewRendererDefinition[]): FilePreviewRendererDefinition;
|
|
33
|
+
/** One async component per renderer, so its chunk loads once and is reused. */
|
|
34
|
+
export declare function filePreviewRendererComponent(renderer: FilePreviewRendererDefinition): Component;
|
|
35
|
+
/** Kind name shown to users: the renderer's own label, the package translation, or the kind. */
|
|
36
|
+
export declare function filePreviewKindLabel(renderer: FilePreviewRendererDefinition, t: UiToolsTranslator): import("../types/index.js").FilePreviewText & string;
|