diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 9a486b5864..0c874fd5ab 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -27,7 +27,7 @@ import { randomColor } from '../utils'; import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu'; import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar'; -import { CalloutBlock, DividerBlock } from './custom-blocks'; +import { CalloutBlock, DividerBlock, ReactEmbedBlock } from './custom-blocks'; export const blockNoteSchema = withPageBreak( BlockNoteSchema.create({ @@ -35,6 +35,7 @@ export const blockNoteSchema = withPageBreak( ...defaultBlockSpecs, callout: CalloutBlock, divider: DividerBlock, + embed: ReactEmbedBlock, }, }), ); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx index 3122b1c175..eb37a9ec4e 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx @@ -14,12 +14,14 @@ import { DocsBlockSchema } from '../types'; import { getCalloutReactSlashMenuItems, getDividerReactSlashMenuItems, + getEmbedReactSlashMenuItems, } from './custom-blocks'; export const BlockNoteSuggestionMenu = () => { const editor = useBlockNoteEditor(); const { t } = useTranslation(); const basicBlocksName = useDictionary().slash_menu.page_break.group; + const advancedBlocksName = useDictionary().slash_menu.table.group; const getSlashMenuItems = useMemo(() => { return async (query: string) => @@ -30,6 +32,7 @@ export const BlockNoteSuggestionMenu = () => { getPageBreakReactSlashMenuItems(editor), getCalloutReactSlashMenuItems(editor, t, basicBlocksName), getDividerReactSlashMenuItems(editor, t, basicBlocksName), + getEmbedReactSlashMenuItems(editor, t, advancedBlocksName), ), query, ), diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/EmbedBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/EmbedBlock.tsx new file mode 100644 index 0000000000..24ee4f6884 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/EmbedBlock.tsx @@ -0,0 +1,215 @@ +import { + FileBlockConfig, + InlineContentSchema, + PropSchema, + StyleSchema, + insertOrUpdateBlock, +} from '@blocknote/core'; +import { + BlockTypeSelectItem, + ReactCustomBlockRenderProps, + ResizableFileBlockWrapper, + createReactBlockSpec, +} from '@blocknote/react'; +import { TFunction } from 'i18next'; +import React, { useEffect, useRef, useState } from 'react'; +import { css } from 'styled-components'; + +import { Box, Icon } from '@/components'; + +import { DocsBlockNoteEditor } from '../../types'; + +export const iframePropSchema: PropSchema & { + caption: { + default: ''; + }; + name: { + default: ''; + }; +} = { + url: { default: '' }, + caption: { default: '' }, + name: { default: '' }, + showPreview: { default: true }, + previewWidth: { default: 500 }, +}; + +export const iframeBlockConfig = { + type: 'embed' as const, + propSchema: iframePropSchema, + content: 'none', + isFileBlock: true, + fileBlockAccept: ['image/png'], +} satisfies FileBlockConfig; + +export const IFrameViewer = ( + props: ReactCustomBlockRenderProps< + typeof iframeBlockConfig, + InlineContentSchema, + StyleSchema + >, +) => { + const url = props.block.props.url; + const aspectRatio = props.block.props.aspectRatio || 16 / 9; + + const [iframeError, setIframeError] = useState(false); + const containerRef = useRef(null); + const [isResizing, setIsResizing] = useState(false); + + useEffect(() => { + if (!containerRef.current) { + return; + } + + const currentEl = containerRef.current as HTMLElement; + const wrapperEl = currentEl.closest('.bn-file-block-content-wrapper'); + if (!wrapperEl) { + return; + } + + const startResizing = () => { + setIsResizing(true); + }; + const stopResizing = () => { + setIsResizing(false); + }; + + wrapperEl.addEventListener('pointerdown', startResizing); + document.addEventListener('pointerup', stopResizing); + + return () => { + wrapperEl.removeEventListener('pointerdown', startResizing); + document.removeEventListener('pointerdown', stopResizing); + }; + }, []); + + if (!url) { + return No URL provided for embed.; + } + + return !iframeError ? ( +
+