@graview/primitives 0.1.35 → 0.1.37
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/app-link-door.d.ts +17 -2
- package/dist/app-link-door.d.ts.map +1 -1
- package/dist/app-link-door.js +0 -6
- package/dist/app-link-door.js.map +1 -1
- package/dist/app-link.d.ts +2 -15
- package/dist/app-link.d.ts.map +1 -1
- package/dist/app-link.js.map +1 -1
- package/dist/files.d.ts +6 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +96 -25
- package/dist/files.js.map +1 -1
- package/dist/first-look-door.d.ts +8 -0
- package/dist/first-look-door.d.ts.map +1 -1
- package/dist/first-look-door.js.map +1 -1
- package/dist/first-look.d.ts +1 -8
- package/dist/first-look.d.ts.map +1 -1
- package/dist/first-look.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/kind-stop.d.ts.map +1 -1
- package/dist/kind-stop.js +2 -2
- package/dist/kind-stop.js.map +1 -1
- package/dist/notify-door.d.ts +1 -19
- package/dist/notify-door.d.ts.map +1 -1
- package/dist/notify-door.js.map +1 -1
- package/dist/notify-sheet.d.ts +8 -1
- package/dist/notify-sheet.d.ts.map +1 -1
- package/dist/notify-sheet.js.map +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.d.ts.map +1 -1
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +10 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/measure.d.ts.map +1 -1
- package/dist/primitives/measure.js +14 -1
- package/dist/primitives/measure.js.map +1 -1
- package/dist/scene.d.ts +1 -1
- package/dist/scene.d.ts.map +1 -1
- package/dist/workbench/activity.d.ts +6 -2
- package/dist/workbench/activity.d.ts.map +1 -1
- package/dist/workbench/activity.js +55 -31
- package/dist/workbench/activity.js.map +1 -1
- package/dist/workbench/answer-args.js +1 -1
- package/dist/workbench/answer-args.js.map +1 -1
- package/package.json +6 -6
package/dist/app-link-door.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
/** What draws one app-link field: the address, how it is drawn, and the record and field it is the value of. */
|
|
3
|
+
export interface AppLinkProps {
|
|
4
|
+
/** The address the record keeps. */
|
|
5
|
+
readonly address: string;
|
|
6
|
+
/** `record`: the name and the home's headline under it; `chip`: the name alone, on a row or a card. */
|
|
7
|
+
readonly as?: "record" | "chip";
|
|
8
|
+
/**
|
|
9
|
+
* The record and field it is the value of, where it may be changed in
|
|
10
|
+
* place: the control that changes it stands beside the link, never
|
|
11
|
+
* around it, and only where an act may write it.
|
|
12
|
+
*/
|
|
13
|
+
readonly nodeId?: string;
|
|
14
|
+
readonly field?: string;
|
|
15
|
+
readonly style?: CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export declare const AppLink: import("react").ComponentType<AppLinkProps>;
|
|
3
18
|
//# sourceMappingURL=app-link-door.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link-door.d.ts","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-link-door.d.ts","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAS3C,gHAAgH;AAChH,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAID,eAAO,MAAM,OAAO,6CAAmH,CAAC"}
|
package/dist/app-link-door.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { retryingImport } from "@graview/core/retry";
|
|
2
2
|
import { lazyModule } from "@graview/react/provider";
|
|
3
|
-
/*
|
|
4
|
-
* THE DOOR TO ANOTHER APP ON A RECORD (FR-171): what draws an app-link
|
|
5
|
-
* field as the app the host's `resolveApp` names — fetched with the first
|
|
6
|
-
* one drawn, never in the first paint, and never at all for an app whose
|
|
7
|
-
* records link none.
|
|
8
|
-
*/
|
|
9
3
|
const APP_LINK = /* @__PURE__ */ lazyModule(retryingImport(() => import("./app-link.js")));
|
|
10
4
|
export const AppLink = APP_LINK.part((m, props) => m.drawAppLink(props), { quiet: true });
|
|
11
5
|
//# sourceMappingURL=app-link-door.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link-door.js","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-link-door.js","sourceRoot":"","sources":["../src/app-link-door.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AA0BrD,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAE3F,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAiC,EAAE,KAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC"}
|
package/dist/app-link.d.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { AppLinkProps } from "./app-link-door.js";
|
|
2
3
|
import "./frame.js";
|
|
3
|
-
export interface AppLinkProps {
|
|
4
|
-
/** The address the record keeps. */
|
|
5
|
-
readonly address: string;
|
|
6
|
-
/** `record`: the name and the home's headline under it; `chip`: the name alone, on a row or a card. */
|
|
7
|
-
readonly as?: "record" | "chip";
|
|
8
|
-
/**
|
|
9
|
-
* The record and field it is the value of, where it may be changed in
|
|
10
|
-
* place: the control that changes it stands beside the link, never
|
|
11
|
-
* around it, and only where an act may write it.
|
|
12
|
-
*/
|
|
13
|
-
readonly nodeId?: string;
|
|
14
|
-
readonly field?: string;
|
|
15
|
-
readonly style?: CSSProperties;
|
|
16
|
-
}
|
|
17
4
|
export declare function AppLink({ address, as, nodeId, field, style }: AppLinkProps): ReactNode;
|
|
18
5
|
/** The part the door draws. */
|
|
19
6
|
export declare const drawAppLink: (props: AppLinkProps) => ReactNode;
|
package/dist/app-link.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link.d.ts","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"app-link.d.ts","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAuD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvD,OAAO,YAAY,CAAC;AA+GpB,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,EAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,YAAY,GAAG,SAAS,CAyD/F;AAmBD,+BAA+B;AAC/B,eAAO,MAAM,WAAW,GAAI,OAAO,YAAY,KAAG,SAAmC,CAAC"}
|
package/dist/app-link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-link.js","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAuD,MAAM,eAAe,CAAC;AAC5H,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"app-link.js","sourceRoot":"","sources":["../src/app-link.tsx"],"names":[],"mappings":"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"}
|
package/dist/files.d.ts
CHANGED
|
@@ -32,12 +32,13 @@ interface Prepared {
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function prepare(file: File, host: HostFiles, options: {
|
|
34
34
|
readonly image: boolean;
|
|
35
|
+
readonly video?: boolean;
|
|
35
36
|
readonly camera?: boolean;
|
|
36
37
|
}): Promise<Prepared | {
|
|
37
38
|
readonly refused: string;
|
|
38
39
|
}>;
|
|
39
40
|
/** Send one prepared file and read what the host answered: the value, or why it was not kept. */
|
|
40
|
-
export declare function send(prepared: Prepared, host: HostFiles, image: boolean): Promise<FileValue | {
|
|
41
|
+
export declare function send(prepared: Prepared, host: HostFiles, image: boolean, video?: boolean): Promise<FileValue | {
|
|
41
42
|
readonly refused: string;
|
|
42
43
|
}>;
|
|
43
44
|
export interface FilesEditorProps {
|
|
@@ -49,6 +50,8 @@ export interface FilesEditorProps {
|
|
|
49
50
|
readonly several: boolean;
|
|
50
51
|
/** Only pictures will do. */
|
|
51
52
|
readonly image: boolean;
|
|
53
|
+
/** Only videos will do (0.1.37). */
|
|
54
|
+
readonly video?: boolean;
|
|
52
55
|
/** The field, in the declaration's words. */
|
|
53
56
|
readonly label: string;
|
|
54
57
|
/** Each file small beside its name: where nothing else draws them (a form). Not under a gallery that already does. */
|
|
@@ -60,7 +63,7 @@ export interface FilesEditorProps {
|
|
|
60
63
|
* the host takes, what it refused and why; without a host's files it says
|
|
61
64
|
* this host takes none, and offers no way to add one.
|
|
62
65
|
*/
|
|
63
|
-
export declare function FilesEditor({ value, onChange, several, image, label, thumbs }: FilesEditorProps): import("react").JSX.Element;
|
|
66
|
+
export declare function FilesEditor({ value, onChange, several, image, video, label, thumbs }: FilesEditorProps): import("react").JSX.Element;
|
|
64
67
|
export interface FileRowProps {
|
|
65
68
|
readonly nodeId: string;
|
|
66
69
|
readonly field: string;
|
|
@@ -105,6 +108,7 @@ export type FilesProps = ({
|
|
|
105
108
|
readonly onChange: (next: unknown) => void;
|
|
106
109
|
readonly several: boolean;
|
|
107
110
|
readonly image: boolean;
|
|
111
|
+
readonly video?: boolean;
|
|
108
112
|
readonly label: string;
|
|
109
113
|
readonly title?: string;
|
|
110
114
|
};
|
package/dist/files.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.tsx"],"names":[],"mappings":"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"}
|
package/dist/files.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { fieldWords, filesOf, isFileValue, isImage, LOCAL_LAYERS } from "@graview/core";
|
|
3
|
-
import { fileFieldsOf, fileWords } from "@graview/core/blocks";
|
|
2
|
+
import { fieldWords, fileSrc, filesOf, isFileValue, isImage, isVideo, LOCAL_LAYERS, videoWords } from "@graview/core";
|
|
3
|
+
import { fileFieldsOf, fileWords, videoClock } from "@graview/core/blocks";
|
|
4
4
|
import { useEditableFields } from "@graview/react/drawing";
|
|
5
5
|
import { useGraview, useHostFiles, useNode, usePlayback } from "@graview/react/provider";
|
|
6
6
|
import { useEffect, useId, useRef, useState } from "react";
|
|
@@ -49,7 +49,7 @@ export function typeWords(file) {
|
|
|
49
49
|
}
|
|
50
50
|
/** The kinds a host takes, in words: "pictures and PDF files". */
|
|
51
51
|
export function kindsWords(kinds) {
|
|
52
|
-
const said = [...new Set(kinds.map((kind) => (kind === "image/*" ? "pictures" : kind === "*/*" || kind === "*" ? "any file" : `${typeWords({ type: kind })} files`)))];
|
|
52
|
+
const said = [...new Set(kinds.map((kind) => (kind === "image/*" ? "pictures" : kind === "video/*" ? "videos" : kind === "*/*" || kind === "*" ? "any file" : `${typeWords({ type: kind })} files`)))];
|
|
53
53
|
return said.length <= 1 ? (said[0] ?? "no files") : `${said.slice(0, -1).join(", ")} and ${said[said.length - 1]}`;
|
|
54
54
|
}
|
|
55
55
|
/** Whether a host's kinds take a type: the type itself, its family (`image/*`), or anything. */
|
|
@@ -61,6 +61,9 @@ export function takes(kinds, type) {
|
|
|
61
61
|
}
|
|
62
62
|
const HEIC = /^image\/hei[cf]$|\.hei[cf]$/i;
|
|
63
63
|
const isHeic = (file) => HEIC.test(file.type) || HEIC.test(file.name);
|
|
64
|
+
/** A video's type from its name's ending, where the browser said none (a phone's `.MOV` often comes untyped). */
|
|
65
|
+
const VIDEO_ENDINGS = { mov: "video/quicktime", mp4: "video/mp4", m4v: "video/mp4", webm: "video/webm", ogv: "video/ogg" };
|
|
66
|
+
const videoTypeOf = (name) => VIDEO_ENDINGS[/\.([a-z0-9]{2,4})$/i.exec(name)?.[1]?.toLowerCase() ?? ""];
|
|
64
67
|
/**
|
|
65
68
|
* A FILE MADE READY TO SEND, or the reason it cannot be, in words.
|
|
66
69
|
*
|
|
@@ -71,11 +74,14 @@ const isHeic = (file) => HEIC.test(file.type) || HEIC.test(file.name);
|
|
|
71
74
|
*/
|
|
72
75
|
export async function prepare(file, host, options) {
|
|
73
76
|
const name = file.name || (options.camera ? "photo.jpg" : "file");
|
|
74
|
-
const type = file.type || (isHeic({ type: "", name }) ? "image/heic" : "application/octet-stream");
|
|
77
|
+
const type = file.type || (isHeic({ type: "", name }) ? "image/heic" : (videoTypeOf(name) ?? "application/octet-stream"));
|
|
75
78
|
const heic = isHeic({ type, name });
|
|
76
79
|
const picture = type.startsWith("image/") || heic;
|
|
77
80
|
if (options.image && !picture)
|
|
78
81
|
return { refused: `“${name}” isn’t a picture.` };
|
|
82
|
+
/* A video goes as it was picked (0.1.37): what a browser plays, its poster and its length are the host's to make. */
|
|
83
|
+
if (options.video)
|
|
84
|
+
return type.startsWith("video/") ? check({ blob: file, name, type }, host) : { refused: `“${name}” isn’t a video.` };
|
|
79
85
|
const redraw = heic || (picture && /^image\/(jpeg|webp)$/.test(type) && (options.camera || file.size > host.maxBytes || file.size > 1_500_000));
|
|
80
86
|
if (redraw) {
|
|
81
87
|
try {
|
|
@@ -111,7 +117,7 @@ function check(prepared, host) {
|
|
|
111
117
|
return prepared;
|
|
112
118
|
}
|
|
113
119
|
/** Send one prepared file and read what the host answered: the value, or why it was not kept. */
|
|
114
|
-
export async function send(prepared, host, image) {
|
|
120
|
+
export async function send(prepared, host, image, video = false) {
|
|
115
121
|
let answered;
|
|
116
122
|
try {
|
|
117
123
|
answered = await host.upload(prepared.blob, prepared.name);
|
|
@@ -124,6 +130,8 @@ export async function send(prepared, host, image) {
|
|
|
124
130
|
return { refused: `The host’s answer for “${prepared.name}” isn’t a file a record can keep.` };
|
|
125
131
|
if (image && !isImage(answered))
|
|
126
132
|
return { refused: `“${prepared.name}” isn’t a picture.` };
|
|
133
|
+
if (video && !isVideo(answered))
|
|
134
|
+
return { refused: `The host kept “${prepared.name}” as ${typeWords(answered)}, not as a video.` };
|
|
127
135
|
const value = { ...answered, ...(answered.width === undefined && prepared.width ? { width: prepared.width } : {}), ...(answered.height === undefined && prepared.height ? { height: prepared.height } : {}) };
|
|
128
136
|
return value;
|
|
129
137
|
}
|
|
@@ -183,17 +191,72 @@ function RemovedLater({ file, style }) {
|
|
|
183
191
|
}
|
|
184
192
|
/** One picture, small: on a card, a row, an `image` block's `thumb`. */
|
|
185
193
|
function FileThumb({ file, size = 56, style }) {
|
|
194
|
+
const host = useHostFiles();
|
|
186
195
|
const [gone, failed] = useRemovedLater(file);
|
|
187
196
|
if (gone)
|
|
188
197
|
return _jsx(RemovedLater, { file: file, style: { width: size, height: size, flex: "none", ...style } });
|
|
189
|
-
|
|
198
|
+
if (isVideo(file))
|
|
199
|
+
return _jsx(VideoThumb, { file: file, size: size, ...(style ? { style } : {}), onError: failed });
|
|
200
|
+
return (_jsx("img", { onError: failed, src: fileSrc(host, file.href), alt: fileWords(file), width: size, height: size, loading: "lazy", decoding: "async", "data-graview-thumb": file.href, style: { width: size, height: size, objectFit: "cover", borderRadius: ROUND, display: "block", flex: "none", background: "var(--graview-edge)", ...style } }));
|
|
201
|
+
}
|
|
202
|
+
/*
|
|
203
|
+
* A VIDEO ON A RECORD (0.1.37). Its length and size are the value's where
|
|
204
|
+
* the host's upload said them, and otherwise what the player read once it
|
|
205
|
+
* loaded the little it fetches before it plays (`preload="metadata"`): said
|
|
206
|
+
* on the page, never written back to the record.
|
|
207
|
+
*/
|
|
208
|
+
function useVideoFacts(file) {
|
|
209
|
+
const [read, setRead] = useState({});
|
|
210
|
+
const duration = file.duration ?? read.duration;
|
|
211
|
+
const facts = { ...(duration !== undefined ? { duration } : {}), width: file.width ?? read.width, height: file.height ?? read.height };
|
|
212
|
+
const onLoadedMetadata = (event) => {
|
|
213
|
+
const video = event.currentTarget;
|
|
214
|
+
setRead({
|
|
215
|
+
...(Number.isFinite(video.duration) && video.duration > 0 ? { duration: video.duration } : {}),
|
|
216
|
+
...(video.videoWidth > 0 && video.videoHeight > 0 ? { width: video.videoWidth, height: video.videoHeight } : {}),
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
return [facts, onLoadedMetadata];
|
|
220
|
+
}
|
|
221
|
+
/** The narrowest a player is drawn where there is room: a small video is made larger, so its controls fit. */
|
|
222
|
+
const MIN_PLAYER = 320;
|
|
223
|
+
/** A video said to somebody who cannot see it: what it shows, and how long it is. */
|
|
224
|
+
const videoLabel = (file, duration) => `${fileWords(file)}, ${videoWords({ ...(duration !== undefined ? { duration } : {}) })}`;
|
|
225
|
+
const PLAY_GLYPH = (_jsx("span", { "data-graview-play-glyph": "", "aria-hidden": "true", style: { position: "absolute", inset: 0, display: "grid", placeItems: "center", pointerEvents: "none" }, children: _jsxs("svg", { viewBox: "0 0 24 24", width: "40%", height: "40%", style: { maxWidth: 40, maxHeight: 40, filter: "drop-shadow(0 1px 2px rgb(0 0 0 / 0.5))" }, children: [_jsx("circle", { cx: "12", cy: "12", r: "11", fill: "rgb(0 0 0 / 0.55)" }), _jsx("path", { d: "M9.5 7.5v9l7-4.5z", fill: "#fff" })] }) }));
|
|
226
|
+
/**
|
|
227
|
+
* A VIDEO, SMALL: on a card, a row, an `image` block's `thumb` — its poster
|
|
228
|
+
* with a play glyph and how long it is. Without a poster, its first frame:
|
|
229
|
+
* a muted player with no controls, out of the keyboard's way, fetching no
|
|
230
|
+
* more than it needs to say its length.
|
|
231
|
+
*/
|
|
232
|
+
function VideoThumb({ file, size, style, onError }) {
|
|
233
|
+
const host = useHostFiles();
|
|
234
|
+
const [facts, onLoadedMetadata] = useVideoFacts(file);
|
|
235
|
+
const box = { width: size, height: size, objectFit: "cover", display: "block", borderRadius: ROUND, background: "var(--graview-edge)" };
|
|
236
|
+
return (_jsxs("span", { role: "img", "aria-label": videoLabel(file, facts.duration), "data-graview-thumb": file.href, "data-graview-video-thumb": "", style: { position: "relative", display: "block", width: size, height: size, flex: "none", ...style }, children: [file.poster ? (_jsx("img", { src: fileSrc(host, file.poster), alt: "", width: size, height: size, loading: "lazy", decoding: "async", onError: onError, style: box })) : (_jsx("video", { src: fileSrc(host, file.href), preload: "metadata", muted: true, playsInline: true, "aria-hidden": "true", tabIndex: -1, width: size, height: size, onLoadedMetadata: onLoadedMetadata, onError: onError, style: box })), PLAY_GLYPH, facts.duration !== undefined ? (_jsx("span", { "data-graview-video-clock": "", "aria-hidden": "true", style: { position: "absolute", right: 3, bottom: 3, padding: "0 4px", borderRadius: 4, background: "rgb(0 0 0 / 0.65)", color: "#fff", fontSize: "0.6875rem", lineHeight: "16px", fontVariantNumeric: "tabular-nums" }, children: videoClock(facts.duration) })) : null] }));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* A VIDEO, PLAYED: its poster first, the browser's own controls — the
|
|
240
|
+
* keyboard reaches them, and they open it full screen — the little it needs
|
|
241
|
+
* fetched before it is played, its captions where the value carries them,
|
|
242
|
+
* and sized from the value so the page does not jump when it loads.
|
|
243
|
+
*/
|
|
244
|
+
function VideoPlayer({ file }) {
|
|
245
|
+
const host = useHostFiles();
|
|
246
|
+
const [gone, failed] = useRemovedLater(file);
|
|
247
|
+
const [facts, onLoadedMetadata] = useVideoFacts(file);
|
|
248
|
+
if (gone)
|
|
249
|
+
return _jsx(RemovedLater, { file: file, style: { width: "100%", aspectRatio: "16 / 9" } });
|
|
250
|
+
const sized = facts.width !== undefined && facts.height !== undefined;
|
|
251
|
+
return (_jsxs("figure", { "data-graview-video-figure": file.href, style: { margin: 0, display: "grid", gap: 4, minWidth: 0 }, children: [_jsx("video", { "data-graview-video": file.href, src: fileSrc(host, file.href), ...(file.poster ? { poster: fileSrc(host, file.poster) } : {}), controls: true, preload: "metadata", playsInline: true, ...(sized ? { width: facts.width, height: facts.height } : {}), "aria-label": videoLabel(file, facts.duration), onLoadedMetadata: onLoadedMetadata, onError: failed, style: { display: "block", width: "100%", maxWidth: sized ? Math.max(facts.width, MIN_PLAYER) : "100%", height: "auto", maxHeight: "80vh", aspectRatio: sized ? `${facts.width} / ${facts.height}` : "16 / 9", borderRadius: ROUND, background: "#000" }, children: file.captions ? _jsx("track", { kind: "captions", src: fileSrc(host, file.captions), srcLang: "en", label: "Captions", default: true }) : null }), _jsx("figcaption", { "data-graview-video-said": "", style: QUIET, children: [file.caption ?? file.name, ...(facts.duration !== undefined ? [videoClock(facts.duration)] : [])].join(" · ") })] }));
|
|
190
252
|
}
|
|
191
253
|
/** One picture of a gallery, opening full size; one removed since the moment drawn is said so, and opens nothing. */
|
|
192
254
|
function GalleryPicture({ file, onOpen }) {
|
|
255
|
+
const host = useHostFiles();
|
|
193
256
|
const [gone, failed] = useRemovedLater(file);
|
|
194
257
|
if (gone)
|
|
195
258
|
return _jsx(RemovedLater, { file: file, style: { width: "100%", aspectRatio: "1" } });
|
|
196
|
-
return (_jsx("button", { type: "button", "aria-label": `Open ${fileWords(file)} full size`, onClick: (event) => onOpen(event.currentTarget), style: { padding: 0, border: 0, background: "none", cursor: "zoom-in", display: "block", width: "100%", borderRadius: ROUND }, children: _jsx("img", { src: file.href, alt: fileWords(file), loading: "lazy", decoding: "async", onError: failed, style: { width: "100%", aspectRatio: "1", objectFit: "cover", borderRadius: ROUND, display: "block", background: "var(--graview-edge)" } }) }));
|
|
259
|
+
return (_jsx("button", { type: "button", "aria-label": `Open ${fileWords(file)} full size`, onClick: (event) => onOpen(event.currentTarget), style: { padding: 0, border: 0, background: "none", cursor: "zoom-in", display: "block", width: "100%", borderRadius: ROUND }, children: _jsx("img", { src: fileSrc(host, file.href), alt: fileWords(file), loading: "lazy", decoding: "async", onError: failed, style: { width: "100%", aspectRatio: "1", objectFit: "cover", borderRadius: ROUND, display: "block", background: "var(--graview-edge)" } }) }));
|
|
197
260
|
}
|
|
198
261
|
/**
|
|
199
262
|
* A RECORD'S FILES, AS ITS PAGE SHOWS THEM: its pictures in a gallery, each
|
|
@@ -201,8 +264,12 @@ function GalleryPicture({ file, onOpen }) {
|
|
|
201
264
|
* documents by name, type and size — a PDF inline, the rest a download.
|
|
202
265
|
*/
|
|
203
266
|
function FilesShown({ files, label }) {
|
|
267
|
+
/* Each file drawn, linked and downloaded where the host serves it (`HostFiles.src`, 0.1.36); said and keyed by the href the record keeps. */
|
|
268
|
+
const host = useHostFiles();
|
|
269
|
+
const at = (file) => fileSrc(host, file.href);
|
|
204
270
|
const pictures = files.filter(isImage);
|
|
205
|
-
const
|
|
271
|
+
const videos = files.filter(isVideo);
|
|
272
|
+
const documents = files.filter((file) => !isImage(file) && !isVideo(file));
|
|
206
273
|
const [open, setOpen] = useState(null);
|
|
207
274
|
const dialog = useRef(null);
|
|
208
275
|
// The picture it opened from: the keyboard goes back there, in an engine that focuses no button on a press (WebKit) too.
|
|
@@ -216,14 +283,14 @@ function FilesShown({ files, label }) {
|
|
|
216
283
|
return (_jsxs("div", { style: { display: "grid", gap: 10, minWidth: 0 }, children: [pictures.length > 0 ? (_jsx("ul", { "data-graview-gallery": "", "aria-label": label, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(96px, 1fr))", gap: 8 }, children: pictures.map((file, index) => (_jsx("li", { style: { minWidth: 0 }, children: _jsx(GalleryPicture, { file: file, onOpen: (button) => {
|
|
217
284
|
opener.current = button;
|
|
218
285
|
setOpen(index);
|
|
219
|
-
} }) }, file.href))) })) : null, documents.length > 0 ? (_jsx("ul", { "data-graview-documents": "", style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: documents.map((file) => (_jsxs("li", { "data-graview-document": file.type, style: { display: "grid", gap: 6, minWidth: 0 }, children: [file.type === "application/pdf" ? (_jsx("object", { data: file
|
|
286
|
+
} }) }, file.href))) })) : null, videos.length > 0 ? (_jsx("ul", { "data-graview-videos": "", "aria-label": label, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 12 }, children: videos.map((file) => (_jsx("li", { style: { minWidth: 0 }, children: _jsx(VideoPlayer, { file: file }) }, file.href))) })) : null, documents.length > 0 ? (_jsx("ul", { "data-graview-documents": "", style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 10 }, children: documents.map((file) => (_jsxs("li", { "data-graview-document": file.type, style: { display: "grid", gap: 6, minWidth: 0 }, children: [file.type === "application/pdf" ? (_jsx("object", { data: at(file), type: "application/pdf", "aria-label": fileWords(file), style: { width: "100%", height: 420, border: "1px solid var(--graview-edge)", borderRadius: ROUND }, children: _jsxs("p", { style: { margin: 0, padding: 12 }, children: ["This browser shows no PDF here: ", _jsxs("a", { href: at(file), download: file.name, children: ["download ", file.name] }), "."] }) })) : null, _jsxs("span", { style: { display: "flex", flexWrap: "wrap", gap: "2px 10px", alignItems: "baseline", minWidth: 0, overflowWrap: "anywhere" }, children: [_jsx("a", { href: at(file), download: file.name, ...(file.type === "application/pdf" ? { target: "_blank", rel: "noopener" } : {}), children: file.name }), _jsxs("span", { style: QUIET, children: [typeWords(file), " \u00B7 ", sizeWords(file.bytes)] })] }), file.caption ? _jsx("span", { style: QUIET, children: file.caption }) : null] }, file.href))) })) : null, shown ? (_jsx("dialog", { ref: dialog, "data-graview-full": shown.href, "aria-label": fileWords(shown), onClose: () => {
|
|
220
287
|
setOpen(null);
|
|
221
288
|
opener.current?.focus();
|
|
222
289
|
}, onClick: (event) => {
|
|
223
290
|
// A press on the dimmed page around the picture closes it.
|
|
224
291
|
if (event.target === event.currentTarget)
|
|
225
292
|
event.currentTarget.close();
|
|
226
|
-
}, style: { padding: 0, border: 0, borderRadius: ROUND, maxWidth: "min(96vw, 1600px)", maxHeight: "94vh", background: "var(--graview-panel)", color: "var(--graview-ink)" }, children: _jsxs("figure", { style: { margin: 0, display: "grid", gap: 8, padding: 10 }, children: [_jsx("img", { src: shown
|
|
293
|
+
}, style: { padding: 0, border: 0, borderRadius: ROUND, maxWidth: "min(96vw, 1600px)", maxHeight: "94vh", background: "var(--graview-panel)", color: "var(--graview-ink)" }, children: _jsxs("figure", { style: { margin: 0, display: "grid", gap: 8, padding: 10 }, children: [_jsx("img", { src: at(shown), alt: fileWords(shown), style: { maxWidth: "100%", maxHeight: "80vh", objectFit: "contain", display: "block", margin: "0 auto" } }), _jsxs("figcaption", { style: { display: "flex", gap: 12, alignItems: "center", justifyContent: "space-between" }, children: [_jsx("span", { children: shown.caption ?? shown.name }), _jsx("form", { method: "dialog", style: { margin: 0 }, children: _jsx("button", { type: "submit", autoFocus: true, children: "Close" }) })] })] }) })) : null] }));
|
|
227
294
|
}
|
|
228
295
|
/**
|
|
229
296
|
* THE FIELD'S EDITOR: pick, take a photo, drop or paste — each sent to the
|
|
@@ -231,7 +298,7 @@ function FilesShown({ files, label }) {
|
|
|
231
298
|
* the host takes, what it refused and why; without a host's files it says
|
|
232
299
|
* this host takes none, and offers no way to add one.
|
|
233
300
|
*/
|
|
234
|
-
export function FilesEditor({ value, onChange, several, image, label, thumbs = true }) {
|
|
301
|
+
export function FilesEditor({ value, onChange, several, image, video = false, label, thumbs = true }) {
|
|
235
302
|
const host = useHostFiles();
|
|
236
303
|
const id = useId();
|
|
237
304
|
const picker = useRef(null);
|
|
@@ -240,11 +307,15 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
240
307
|
const [said, setSaid] = useState(null);
|
|
241
308
|
const [refused, setRefused] = useState([]);
|
|
242
309
|
const [over, setOver] = useState(false);
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
const
|
|
310
|
+
/* What only pictures, or only videos, will do with: the host's kinds of that family. */
|
|
311
|
+
const family = image ? "image/" : video ? "video/" : undefined;
|
|
312
|
+
const kinds = host ? (family ? host.kinds.filter((kind) => kind.startsWith(family) || kind === "*" || kind === "*/*") : host.kinds) : [];
|
|
313
|
+
const familyKinds = host ? host.kinds.length === 0 || kinds.length > 0 : false;
|
|
314
|
+
const pictureKinds = !video && (host ? host.kinds.length === 0 || (image ? kinds : host.kinds).some((kind) => kind.startsWith("image/") || kind.startsWith("*")) : false);
|
|
315
|
+
const open = Boolean(host) && (!family || familyKinds);
|
|
316
|
+
const sort = image ? "picture" : video ? "video" : "file";
|
|
317
|
+
const noun = several ? `${sort}s` : `a ${sort}`;
|
|
318
|
+
const accept = family ? (kinds.length > 0 ? kinds.join(",") : `${family}*`) : host?.kinds.join(",") || undefined;
|
|
248
319
|
const coarse = typeof matchMedia === "function" && matchMedia("(pointer: coarse)").matches;
|
|
249
320
|
// What the field holds as the upload answers, which somebody else may have changed while it was sent.
|
|
250
321
|
const latest = useRef(value);
|
|
@@ -270,17 +341,17 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
270
341
|
const kept = [];
|
|
271
342
|
const problems = [];
|
|
272
343
|
if (chosen.length < given.length)
|
|
273
|
-
problems.push(`${label} holds one ${
|
|
344
|
+
problems.push(`${label} holds one ${sort}, so ${given.slice(1).map((file) => `“${file.name || "the photo"}”`).join(", ")} ${given.length > 2 ? "were" : "was"} left out.`);
|
|
274
345
|
setRefused([]);
|
|
275
346
|
setSaid(null);
|
|
276
347
|
for (const file of chosen) {
|
|
277
348
|
setBusy(`Sending ${file.name || "the photo"}…`);
|
|
278
|
-
const prepared = await prepare(file, host, { image, camera: from === "camera" });
|
|
349
|
+
const prepared = await prepare(file, host, { image, video, camera: from === "camera" });
|
|
279
350
|
if ("refused" in prepared) {
|
|
280
351
|
problems.push(prepared.refused);
|
|
281
352
|
continue;
|
|
282
353
|
}
|
|
283
|
-
const sent = await send(prepared, host, image);
|
|
354
|
+
const sent = await send(prepared, host, image, video);
|
|
284
355
|
if ("refused" in sent)
|
|
285
356
|
problems.push(sent.refused);
|
|
286
357
|
// The same bytes answer the same address: a file already here is said, not held twice.
|
|
@@ -332,15 +403,15 @@ export function FilesEditor({ value, onChange, several, image, label, thumbs = t
|
|
|
332
403
|
return;
|
|
333
404
|
event.preventDefault();
|
|
334
405
|
setOver(true);
|
|
335
|
-
}, onDragLeave: () => setOver(false), onDrop: onDrop, style: { display: "grid", gap: 8, minWidth: 0, padding: over ? 6 : 0, outline: over ? "2px dashed var(--graview-accent)" : "none", borderRadius: ROUND }, children: [_jsx("span", { id: `${id}-label`, style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, children: label }), value.length > 0 && (several || value.length > 1) ? (_jsx("ul", { ref: list, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 6 }, children: value.map((file, index) => (_jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", minWidth: 0 }, children: [thumbs && isImage(file) ? _jsx(FileThumb, { file: file, size: 40 }) : null, _jsx("span", { style: { flex: "1 1 8rem", minWidth: 0, overflowWrap: "anywhere" }, children: file.name }), _jsx(CaptionInput, { file: file, onCaption: (caption) => change(value.map((one, at) => (at === index ? withCaption(one, caption) : one)), caption ? `${file.name} now says “${caption}”.` : `${file.name} says its name again.`) }), several && index > 0 ? (_jsx("button", { type: "button", "data-graview-file-move": "", "aria-label": `Move ${file.name} earlier`, onClick: () => change(value.map((one, at) => (at === index - 1 ? file : at === index ? value[index - 1] : one)), `Moved ${file.name} earlier.`, { at: index - 1, what: "move" }), children: "\u2191" })) : null, several ? (_jsx("button", { type: "button", "data-graview-file-remove": "", "aria-label": `Remove ${file.name}`, onClick: () => change(value.filter((_, at) => at !== index), `Removed ${file.name}.`, { at: index, what: "remove" }), children: "Remove" })) : null] }, `${index}:${file.href}`))) })) : value.length === 1 ? (_jsx(CaptionInput, { file: value[0], onCaption: (caption) => change([withCaption(value[0], caption)], caption ? `${value[0].name} now says “${caption}”.` : `${value[0].name} says its name again.`) })) : null, open ? (_jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [_jsx("input", { ref: picker, type: "file", hidden: true, multiple: several, ...(accept ? { accept } : {}), "data-graview-file-input": "", "aria-label": `Choose ${noun}`, onChange: (event) => {
|
|
406
|
+
}, onDragLeave: () => setOver(false), onDrop: onDrop, style: { display: "grid", gap: 8, minWidth: 0, padding: over ? 6 : 0, outline: over ? "2px dashed var(--graview-accent)" : "none", borderRadius: ROUND }, children: [_jsx("span", { id: `${id}-label`, style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, children: label }), value.length > 0 && (several || value.length > 1) ? (_jsx("ul", { ref: list, style: { listStyle: "none", margin: 0, padding: 0, display: "grid", gap: 6 }, children: value.map((file, index) => (_jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", minWidth: 0 }, children: [thumbs && (isImage(file) || isVideo(file)) ? _jsx(FileThumb, { file: file, size: 40 }) : null, _jsx("span", { style: { flex: "1 1 8rem", minWidth: 0, overflowWrap: "anywhere" }, children: file.name }), _jsx(CaptionInput, { file: file, onCaption: (caption) => change(value.map((one, at) => (at === index ? withCaption(one, caption) : one)), caption ? `${file.name} now says “${caption}”.` : `${file.name} says its name again.`) }), several && index > 0 ? (_jsx("button", { type: "button", "data-graview-file-move": "", "aria-label": `Move ${file.name} earlier`, onClick: () => change(value.map((one, at) => (at === index - 1 ? file : at === index ? value[index - 1] : one)), `Moved ${file.name} earlier.`, { at: index - 1, what: "move" }), children: "\u2191" })) : null, several ? (_jsx("button", { type: "button", "data-graview-file-remove": "", "aria-label": `Remove ${file.name}`, onClick: () => change(value.filter((_, at) => at !== index), `Removed ${file.name}.`, { at: index, what: "remove" }), children: "Remove" })) : null] }, `${index}:${file.href}`))) })) : value.length === 1 ? (_jsx(CaptionInput, { file: value[0], onCaption: (caption) => change([withCaption(value[0], caption)], caption ? `${value[0].name} now says “${caption}”.` : `${value[0].name} says its name again.`) })) : null, open ? (_jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }, children: [_jsx("input", { ref: picker, type: "file", hidden: true, multiple: several, ...(accept ? { accept } : {}), "data-graview-file-input": "", "aria-label": `Choose ${noun}`, onChange: (event) => {
|
|
336
407
|
const picked = filesFrom(event.currentTarget.files);
|
|
337
408
|
event.currentTarget.value = "";
|
|
338
409
|
void add(picked, "picked");
|
|
339
|
-
} }), _jsx("button", { type: "button", disabled: busy !== null, onClick: () => picker.current?.click(), "data-graview-file-choose": "", children: value.length > 0 && !several ? `Replace ${
|
|
410
|
+
} }), _jsx("button", { type: "button", disabled: busy !== null, onClick: () => picker.current?.click(), "data-graview-file-choose": "", children: value.length > 0 && !several ? `Replace the ${sort}` : `Choose ${noun}` }), pictureKinds ? (_jsxs(_Fragment, { children: [_jsx("input", { ref: camera, type: "file", hidden: true, accept: "image/*", capture: "environment", "data-graview-photo-input": "", "aria-label": "Take a photo", onChange: (event) => {
|
|
340
411
|
const taken = filesFrom(event.currentTarget.files);
|
|
341
412
|
event.currentTarget.value = "";
|
|
342
413
|
void add(taken, "camera");
|
|
343
|
-
} }), coarse ? (_jsx("button", { type: "button", disabled: busy !== null, onClick: () => camera.current?.click(), children: "Take a photo" })) : null] })) : null, _jsxs("span", { style: QUIET, "data-graview-file-takes": "", children: ["or drop or paste ", several ? "them" : "it", " here \u00B7 ", host.kinds.length > 0 ? kindsWords(kinds.length > 0 ? kinds : host.kinds) : "any file", ", up to ", sizeWords(host.maxBytes)] })] })) : (_jsx("p", { style: { ...QUIET, margin: 0 }, "data-graview-file-none": "", children: host ?
|
|
414
|
+
} }), coarse ? (_jsx("button", { type: "button", disabled: busy !== null, onClick: () => camera.current?.click(), children: "Take a photo" })) : null] })) : null, _jsxs("span", { style: QUIET, "data-graview-file-takes": "", children: ["or drop or paste ", several ? "them" : "it", " here \u00B7 ", host.kinds.length > 0 ? kindsWords(kinds.length > 0 ? kinds : host.kinds) : "any file", ", up to ", sizeWords(host.maxBytes)] })] })) : (_jsx("p", { style: { ...QUIET, margin: 0 }, "data-graview-file-none": "", children: host ? `This host doesn’t take ${video ? "videos" : "pictures"}, so none can be added here.` : "This host doesn’t take files, so none can be added here." })), _jsx("p", { "aria-live": "polite", style: { ...QUIET, margin: 0, minHeight: busy || said ? undefined : 0 }, "data-graview-file-status": "", children: busy ?? said ?? "" }), refused.length > 0 ? (_jsx("div", { role: "alert", "data-graview-file-refused": "", style: { color: "var(--graview-warn)", fontSize: "0.875rem", display: "grid", gap: 2 }, children: refused.map((line, index) => (_jsx("p", { style: { margin: 0 }, children: line }, index))) })) : null] }));
|
|
344
415
|
}
|
|
345
416
|
const withCaption = (file, caption) => {
|
|
346
417
|
const { caption: _was, ...rest } = file;
|
|
@@ -355,7 +426,7 @@ function CaptionInput({ file, onCaption }) {
|
|
|
355
426
|
if (next !== (file.caption ?? ""))
|
|
356
427
|
onCaption(next);
|
|
357
428
|
};
|
|
358
|
-
return (_jsx("input", { type: "text", value: draft, placeholder: isImage(file) ? "What it shows" : "What it is", "aria-label": `What ${file.name} ${isImage(file) ? "shows" : "is"}`, "data-graview-file-caption": file.href, onChange: (event) => setDraft(event.target.value), onBlur: keep, onKeyDown: (event) => {
|
|
429
|
+
return (_jsx("input", { type: "text", value: draft, placeholder: isImage(file) || isVideo(file) ? "What it shows" : "What it is", "aria-label": `What ${file.name} ${isImage(file) || isVideo(file) ? "shows" : "is"}`, "data-graview-file-caption": file.href, onChange: (event) => setDraft(event.target.value), onBlur: keep, onKeyDown: (event) => {
|
|
359
430
|
if (event.key === "Enter") {
|
|
360
431
|
event.preventDefault();
|
|
361
432
|
keep();
|
|
@@ -381,7 +452,7 @@ function FileField(props) {
|
|
|
381
452
|
const several = shape?.type === "several";
|
|
382
453
|
if (files.length === 0 && one?.type !== "file")
|
|
383
454
|
return null;
|
|
384
|
-
return (_jsxs(Row, { ...props, children: [_jsx(FilesShown, { files: files, label: label }), editable && one?.type === "file" ? (_jsx(FilesEditor, { value: files, thumbs: false, several: several, image: one.image === true, label: label, onChange: (next) => {
|
|
455
|
+
return (_jsxs(Row, { ...props, children: [_jsx(FilesShown, { files: files, label: label }), editable && one?.type === "file" ? (_jsx(FilesEditor, { value: files, thumbs: false, several: several, image: one.image === true, video: one.video === true, label: label, onChange: (next) => {
|
|
385
456
|
// One file: what was added replaces it; an edit cannot leave a field with nothing.
|
|
386
457
|
const value = several ? next : next[next.length - 1];
|
|
387
458
|
if (value !== undefined)
|
|
@@ -400,7 +471,7 @@ export function drawFiles(props) {
|
|
|
400
471
|
// A gallery's pictures are pressed, so they stand above the card's own link.
|
|
401
472
|
return props.files.length > 0 ? (_jsx("span", { className: "graview-spec-image", "data-graview-field": props.field, "data-graview-image": props.view, style: { display: "block", minWidth: 0, ...(props.view === "gallery" ? { position: "relative", zIndex: LOCAL_LAYERS.raised } : {}) }, children: props.view === "thumb" ? _jsx(FileThumb, { file: props.files[0], size: 72 }) : _jsx(FilesShown, { files: props.files, label: props.label }) })) : null;
|
|
402
473
|
case "control": {
|
|
403
|
-
const editor = _jsx(FilesEditor, { value: filesOf(props.value), several: props.several, image: props.image, label: props.label, onChange: (next) => props.onChange(props.several ? next : next[next.length - 1]) });
|
|
474
|
+
const editor = _jsx(FilesEditor, { value: filesOf(props.value), several: props.several, image: props.image, video: props.video === true, label: props.label, onChange: (next) => props.onChange(props.several ? next : next[next.length - 1]) });
|
|
404
475
|
return props.title === undefined ? (editor) : (_jsxs("fieldset", { "data-graview-part": "group", "data-graview-field": props.several ? "files" : "file", children: [_jsx("legend", { "data-graview-part": "label", children: props.title }), editor] }));
|
|
405
476
|
}
|
|
406
477
|
}
|