UNPKG

@blocknote/core

Version:

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

212 lines (191 loc) 6.69 kB
import type { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js"; import { BlockFromConfig, FileBlockConfig } from "../../../../schema/index.js"; import { createFileBlockWrapper } from "./createFileBlockWrapper.js"; export const createResizableFileBlockWrapper = ( block: BlockFromConfig<FileBlockConfig, any, any>, editor: BlockNoteEditor<any, any, any>, element: { dom: HTMLElement; destroy?: () => void }, resizeHandlesContainerElement: HTMLElement, buttonText: string, buttonIcon: HTMLElement, ): { dom: HTMLElement; destroy: () => void } => { const { dom, destroy } = createFileBlockWrapper( block, editor, element, buttonText, buttonIcon, ); const wrapper = dom; if (block.props.url && block.props.showPreview) { if (block.props.previewWidth) { wrapper.style.width = `${block.props.previewWidth}px`; } else { wrapper.style.width = "fit-content"; } } const leftResizeHandle = document.createElement("div"); leftResizeHandle.className = "bn-resize-handle"; leftResizeHandle.style.left = "4px"; const rightResizeHandle = document.createElement("div"); rightResizeHandle.className = "bn-resize-handle"; rightResizeHandle.style.right = "4px"; // Temporary parameters set when the user begins resizing the element, used to // calculate the new width of the element. let resizeParams: | { handleUsed: "left" | "right"; initialWidth: number; initialClientX: number; } | undefined; let width = block.props.previewWidth! as number; // Updates the element width with an updated width depending on the cursor X // offset from when the resize began, and which resize handle is being used. const windowMouseMoveHandler = (event: MouseEvent) => { if (!resizeParams) { if ( !editor.isEditable && resizeHandlesContainerElement.contains(leftResizeHandle) && resizeHandlesContainerElement.contains(rightResizeHandle) ) { resizeHandlesContainerElement.removeChild(leftResizeHandle); resizeHandlesContainerElement.removeChild(rightResizeHandle); } return; } let newWidth: number; if (block.props.textAlignment === "center") { if (resizeParams.handleUsed === "left") { newWidth = resizeParams.initialWidth + (resizeParams.initialClientX - event.clientX) * 2; } else { newWidth = resizeParams.initialWidth + (event.clientX - resizeParams.initialClientX) * 2; } } else { if (resizeParams.handleUsed === "left") { newWidth = resizeParams.initialWidth + resizeParams.initialClientX - event.clientX; } else { newWidth = resizeParams.initialWidth + event.clientX - resizeParams.initialClientX; } } // Min element width in px. const minWidth = 64; // Ensures the element is not wider than the editor and not narrower than a // predetermined minimum width. width = Math.min( Math.max(newWidth, minWidth), editor.domElement?.firstElementChild?.clientWidth || Number.MAX_VALUE, ); wrapper.style.width = `${width}px`; }; // Stops mouse movements from resizing the element and updates the block's // `width` prop to the new value. const windowMouseUpHandler = (event: MouseEvent) => { // Hides the drag handles if the cursor is no longer over the element. if ( (!event.target || !wrapper.contains(event.target as Node) || !editor.isEditable) && resizeHandlesContainerElement.contains(leftResizeHandle) && resizeHandlesContainerElement.contains(rightResizeHandle) ) { resizeHandlesContainerElement.removeChild(leftResizeHandle); resizeHandlesContainerElement.removeChild(rightResizeHandle); } if (!resizeParams) { return; } resizeParams = undefined; editor.updateBlock(block, { props: { previewWidth: width, }, }); }; // Shows the resize handles when hovering over the wrapper with the cursor. const wrapperMouseEnterHandler = () => { if (editor.isEditable) { resizeHandlesContainerElement.appendChild(leftResizeHandle); resizeHandlesContainerElement.appendChild(rightResizeHandle); } }; // Hides the resize handles when the cursor leaves the wrapper, unless the // cursor moves to one of the resize handles. const wrapperMouseLeaveHandler = (event: MouseEvent) => { if ( event.relatedTarget === leftResizeHandle || event.relatedTarget === rightResizeHandle ) { return; } if (resizeParams) { return; } if ( editor.isEditable && resizeHandlesContainerElement.contains(leftResizeHandle) && resizeHandlesContainerElement.contains(rightResizeHandle) ) { resizeHandlesContainerElement.removeChild(leftResizeHandle); resizeHandlesContainerElement.removeChild(rightResizeHandle); } }; // Sets the resize params, allowing the user to begin resizing the element by // moving the cursor left or right. const leftResizeHandleMouseDownHandler = (event: MouseEvent) => { event.preventDefault(); resizeParams = { handleUsed: "left", initialWidth: wrapper.clientWidth, initialClientX: event.clientX, }; }; const rightResizeHandleMouseDownHandler = (event: MouseEvent) => { event.preventDefault(); resizeParams = { handleUsed: "right", initialWidth: wrapper.clientWidth, initialClientX: event.clientX, }; }; window.addEventListener("mousemove", windowMouseMoveHandler); window.addEventListener("mouseup", windowMouseUpHandler); wrapper.addEventListener("mouseenter", wrapperMouseEnterHandler); wrapper.addEventListener("mouseleave", wrapperMouseLeaveHandler); leftResizeHandle.addEventListener( "mousedown", leftResizeHandleMouseDownHandler, ); rightResizeHandle.addEventListener( "mousedown", rightResizeHandleMouseDownHandler, ); return { dom: wrapper, destroy: () => { destroy?.(); window.removeEventListener("mousemove", windowMouseMoveHandler); window.removeEventListener("mouseup", windowMouseUpHandler); wrapper.removeEventListener("mouseenter", wrapperMouseEnterHandler); wrapper.removeEventListener("mouseleave", wrapperMouseLeaveHandler); leftResizeHandle.removeEventListener( "mousedown", leftResizeHandleMouseDownHandler, ); rightResizeHandle.removeEventListener( "mousedown", rightResizeHandleMouseDownHandler, ); }, }; };