UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

160 lines (139 loc) 4.55 kB
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js"; import { BlockFromConfig, createBlockSpec, FileBlockConfig, Props, PropSchema, } from "../../schema/index.js"; import { defaultProps } from "../defaultProps.js"; import { parseFigureElement } from "../FileBlockContent/helpers/parse/parseFigureElement.js"; import { createFigureWithCaption } from "../FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.js"; import { createLinkWithCaption } from "../FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.js"; import { createResizableFileBlockWrapper } from "../FileBlockContent/helpers/render/createResizableFileBlockWrapper.js"; import { parseVideoElement } from "./parseVideoElement.js"; export const FILE_VIDEO_ICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 5V19H16V5H8ZM4 5V7H6V5H4ZM18 5V7H20V5H18ZM4 9V11H6V9H4ZM18 9V11H20V9H18ZM4 13V15H6V13H4ZM18 13V15H20V13H18ZM4 17V19H6V17H4ZM18 17V19H20V17H18Z"></path></svg>'; export const videoPropSchema = { textAlignment: defaultProps.textAlignment, backgroundColor: defaultProps.backgroundColor, // File name. name: { default: "" as const, }, // File url. url: { default: "" as const, }, // File caption. caption: { default: "" as const, }, showPreview: { default: true, }, // File preview width in px. previewWidth: { default: undefined, type: "number", }, } satisfies PropSchema; export const videoBlockConfig = { type: "video" as const, propSchema: videoPropSchema, content: "none", isFileBlock: true, fileBlockAccept: ["video/*"], } satisfies FileBlockConfig; export const videoRender = ( block: BlockFromConfig<typeof videoBlockConfig, any, any>, editor: BlockNoteEditor<any, any, any>, ) => { const icon = document.createElement("div"); icon.innerHTML = 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; video.width = block.props.previewWidth; videoWrapper.appendChild(video); return createResizableFileBlockWrapper( block, editor, { dom: videoWrapper }, videoWrapper, editor.dictionary.file_blocks.video.add_button_text, icon.firstElementChild as HTMLElement, ); }; export const videoParse = ( element: HTMLElement, ): Partial<Props<typeof videoBlockConfig.propSchema>> | undefined => { if (element.tagName === "VIDEO") { // Ignore if parent figure has already been parsed. if (element.closest("figure")) { return undefined; } return parseVideoElement(element as HTMLVideoElement); } if (element.tagName === "FIGURE") { const parsedFigure = parseFigureElement(element, "video"); if (!parsedFigure) { return undefined; } const { targetElement, caption } = parsedFigure; return { ...parseVideoElement(targetElement as HTMLVideoElement), caption, }; } return undefined; }; export const videoToExternalHTML = ( block: BlockFromConfig<typeof videoBlockConfig, any, any>, ) => { if (!block.props.url) { const div = document.createElement("p"); div.textContent = "Add video"; return { dom: div, }; } 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, }; }; export const VideoBlock = createBlockSpec(videoBlockConfig, { render: videoRender, parse: videoParse, toExternalHTML: videoToExternalHTML, });