Open serpent213 opened 4 days ago
As part of a website-CMS I needed a way to define a background image for an element and overlay two blocks of text. This is what I came up with:
// import type { FileBlockConfig } from "@blocknote/core" import { createReactBlockSpec, FilePanel } from "@blocknote/react" import { useEffect, useRef, useState } from "react" // interface FileAndContentBlockConfig extends Omit<FileBlockConfig, "content"> { // content: "inline" // } export const BackgroundScroller = createReactBlockSpec( { type: "backgroundScroller", propSchema: { // caption: { // default: "" // }, heading: { default: "" }, url: { default: "" }, name: { default: "" } }, content: "inline", // isFileBlock: true, fileBlockAccept: ["image/*"] }, { render: (props) => { const [isFilePanelOpen, setIsFilePanelOpen] = useState(false) const inputClassName = "block !p-2 !text-center !text-white bg-gray-600/60 rounded-lg" const filePanelRef = useRef<HTMLDivElement>(null) // Update non-content props const handleUpdate = (propName: "heading") => (e: React.ChangeEvent<HTMLInputElement>) => { props.editor.updateBlock(props.block, { type: "backgroundScroller", props: { ...props.block.props, [propName]: e.target.value } }) } // Close file panel when clicking outside of it useEffect(() => { const handleClickOutside = (event: MouseEvent | TouchEvent) => { if (filePanelRef.current && !filePanelRef.current.contains(event.target as Node)) { setIsFilePanelOpen(false) } } if (isFilePanelOpen) { document.addEventListener("mousedown", handleClickOutside) document.addEventListener("touchstart", handleClickOutside) } else { document.removeEventListener("mousedown", handleClickOutside) document.removeEventListener("touchstart", handleClickOutside) } return () => { document.removeEventListener("mousedown", handleClickOutside) document.removeEventListener("touchstart", handleClickOutside) } }, [isFilePanelOpen]) // Close file panel after file upload // biome-ignore lint/correctness/useExhaustiveDependencies: 🤷🏼♂️ useEffect(() => { setIsFilePanelOpen(false) }, [props.block.props.url]) return ( <div className="relative min-h-52 !p-0 flex flex-col justify-center items-center flex-grow rounded space-y-3 bg-lime-500" style={ props.block.props.url && (props.block.props.url as string).length > 0 ? { backgroundImage: `url('${props.block.props.url}')`, backgroundSize: "cover", backgroundPosition: "center", backgroundRepeat: "no-repeat" } : {} } > <input type="text" value={props.block.props.heading} placeholder="Heading" className={inputClassName} size={(props.block.props.heading && (props.block.props.heading as string).length) || 10} onChange={handleUpdate("heading")} /> <div className={ "inline-content text-2xl font-medium text-center text-white bg-gray-600/60 p-2 rounded-lg" } ref={props.contentRef} /> <button type="button" className="absolute top-2 right-2 !m-0 !btn !btn-sm !btn-primary" onClick={() => setIsFilePanelOpen(true)} > Bild hochladen </button> {isFilePanelOpen && ( <div className="absolute top-9 right-2" ref={filePanelRef}> {/* @ts-ignore */} <FilePanel block={props.block} /> </div> )} </div> ) } } )
This seems to work fine, but feels slightly hacky: I had to suppress one TS and one Biome error and probably there is a better way to implement the handleClickOutside. Would be lovely to have an example in this direction. 🙂
handleClickOutside
As part of a website-CMS I needed a way to define a background image for an element and overlay two blocks of text. This is what I came up with:
This seems to work fine, but feels slightly hacky: I had to suppress one TS and one Biome error and probably there is a better way to implement the
handleClickOutside
. Would be lovely to have an example in this direction. 🙂