import { createBlockConfig, createBlockSpec } from "../../schema/index.js";
import { defaultProps, parseDefaultProps } from "../defaultProps.js";
import { parseFigureElement } from "../File/helpers/parse/parseFigureElement.js";
import { createResizableFileBlockWrapper } from "../File/helpers/render/createResizableFileBlockWrapper.js";
import { createFigureWithCaption } from "../File/helpers/toExternalHTML/createFigureWithCaption.js";
import { createLinkWithCaption } from "../File/helpers/toExternalHTML/createLinkWithCaption.js";
import { parseVideoElement } from "./parseVideoElement.js";
export const FILE_VIDEO_ICON_SVG =
'';
export interface VideoOptions {
icon?: string;
}
export type VideoBlockConfig = ReturnType;
export const createVideoBlockConfig = createBlockConfig(
(_ctx: VideoOptions) => ({
type: "video" as const,
propSchema: {
textAlignment: defaultProps.textAlignment,
backgroundColor: defaultProps.backgroundColor,
name: { default: "" as const },
url: { default: "" as const },
caption: { default: "" as const },
showPreview: { default: true },
previewWidth: { default: undefined, type: "number" as const },
},
content: "none" as const,
}),
);
export const videoParse = (_config: VideoOptions) => (element: HTMLElement) => {
if (element.tagName === "VIDEO") {
// Ignore if parent figure has already been parsed.
if (element.closest("figure")) {
return undefined;
}
const { backgroundColor } = parseDefaultProps(element);
return {
...parseVideoElement(element as HTMLVideoElement),
backgroundColor,
};
}
if (element.tagName === "FIGURE") {
const parsedFigure = parseFigureElement(element, "video");
if (!parsedFigure) {
return undefined;
}
const { targetElement, caption } = parsedFigure;
const { backgroundColor } = parseDefaultProps(element);
return {
...parseVideoElement(targetElement as HTMLVideoElement),
backgroundColor,
caption,
};
}
return undefined;
};
export const createVideoBlockSpec = createBlockSpec(
createVideoBlockConfig,
(config) => ({
meta: {
fileBlockAccept: ["video/*"],
},
parse: videoParse(config),
render(block, editor) {
const icon = document.createElement("div");
icon.innerHTML = config.icon ?? FILE_VIDEO_ICON_SVG;
const videoWrapper = document.createElement("div");
videoWrapper.className = "bn-visual-media-wrapper";
const video = document.createElement("video");
video.className = "bn-visual-media";
if (editor.resolveFileUrl) {
editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
video.src = downloadUrl;
});
} else {
video.src = block.props.url;
}
video.controls = true;
video.contentEditable = "false";
video.draggable = false;
if (block.props.previewWidth) {
video.width = block.props.previewWidth;
}
videoWrapper.appendChild(video);
return createResizableFileBlockWrapper(
block,
editor,
{ dom: videoWrapper },
videoWrapper,
icon.firstElementChild as HTMLElement,
);
},
toExternalHTML(block) {
if (!block.props.url) {
return {
dom: document.createElement("video"),
};
}
let video;
if (block.props.showPreview) {
video = document.createElement("video");
video.src = block.props.url;
if (block.props.previewWidth) {
video.width = block.props.previewWidth;
}
} else {
video = document.createElement("a");
video.href = block.props.url;
video.textContent = block.props.name || block.props.url;
}
if (block.props.caption) {
if (block.props.showPreview) {
return createFigureWithCaption(video, block.props.caption);
} else {
return createLinkWithCaption(video, block.props.caption);
}
}
return {
dom: video,
};
},
runsBefore: ["file"],
}),
);