import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js"; import { BlockFromConfig, createBlockConfig, createBlockSpec, } from "../../schema/index.js"; import { defaultProps, parseDefaultProps } from "../defaultProps.js"; import { parseFigureElement } from "../File/helpers/parse/parseFigureElement.js"; import { createFileBlockWrapper } from "../File/helpers/render/createFileBlockWrapper.js"; import { createFigureWithCaption } from "../File/helpers/toExternalHTML/createFigureWithCaption.js"; import { createLinkWithCaption } from "../File/helpers/toExternalHTML/createLinkWithCaption.js"; import { parseAudioElement } from "./parseAudioElement.js"; export const FILE_AUDIO_ICON_SVG = ''; export interface AudioOptions { icon?: string; } export type AudioBlockConfig = ReturnType; export const createAudioBlockConfig = createBlockConfig( (_ctx: AudioOptions) => ({ type: "audio" as const, propSchema: { backgroundColor: defaultProps.backgroundColor, // File name. name: { default: "" as const, }, // File url. url: { default: "" as const, }, // File caption. caption: { default: "" as const, }, showPreview: { default: true, }, }, content: "none", }) as const, ); export const audioParse = (_config: AudioOptions = {}) => (element: HTMLElement) => { if (element.tagName === "AUDIO") { // Ignore if parent figure has already been parsed. if (element.closest("figure")) { return undefined; } const { backgroundColor } = parseDefaultProps(element); return { ...parseAudioElement(element as HTMLAudioElement), backgroundColor, }; } if (element.tagName === "FIGURE") { const parsedFigure = parseFigureElement(element, "audio"); if (!parsedFigure) { return undefined; } const { targetElement, caption } = parsedFigure; const { backgroundColor } = parseDefaultProps(element); return { ...parseAudioElement(targetElement as HTMLAudioElement), backgroundColor, caption, }; } return undefined; }; export const audioRender = (config: AudioOptions = {}) => ( block: BlockFromConfig, any, any>, editor: BlockNoteEditor< Record<"audio", ReturnType>, any, any >, ) => { const icon = document.createElement("div"); icon.innerHTML = config.icon ?? FILE_AUDIO_ICON_SVG; const audio = document.createElement("audio"); audio.className = "bn-audio"; if (editor.resolveFileUrl) { editor.resolveFileUrl(block.props.url).then((downloadUrl) => { audio.src = downloadUrl; }); } else { audio.src = block.props.url; } audio.controls = true; audio.contentEditable = "false"; audio.draggable = false; return createFileBlockWrapper( block, editor, { dom: audio }, icon.firstElementChild as HTMLElement, ); }; export const audioToExternalHTML = (_config: AudioOptions = {}) => ( block: BlockFromConfig, any, any>, _editor: BlockNoteEditor< Record<"audio", ReturnType>, any, any >, ) => { if (!block.props.url) { return { dom: document.createElement("audio"), }; } let audio; if (block.props.showPreview) { audio = document.createElement("audio"); audio.src = block.props.url; } else { audio = document.createElement("a"); audio.href = block.props.url; audio.textContent = block.props.name || block.props.url; } if (block.props.caption) { if (block.props.showPreview) { return createFigureWithCaption(audio, block.props.caption); } else { return createLinkWithCaption(audio, block.props.caption); } } return { dom: audio, }; }; export const createAudioBlockSpec = createBlockSpec( createAudioBlockConfig, (config) => ({ meta: { fileBlockAccept: ["audio/*"], }, parse: audioParse(config), render: audioRender(config), toExternalHTML: audioToExternalHTML(config), runsBefore: ["file"], }), );