casualroom/apps/fenghuo/web/components/tiptap-ui/image-upload-button/image-upload-button.tsx

130 lines
2.8 KiB
TypeScript
Executable File

"use client"
import * as React from "react"
import { type Editor } from "@tiptap/react"
// --- Hooks ---
import { useTiptapEditor } from "@/hooks/use-tiptap-editor"
// --- Icons ---
import { ImagePlusIcon } from "@/components/tiptap-icons/image-plus-icon"
// --- UI Primitives ---
import type { ButtonProps } from "@/components/tiptap-ui-primitive/button"
import { Button } from "@/components/tiptap-ui-primitive/button"
export interface ImageUploadButtonProps extends ButtonProps {
editor?: Editor | null
text?: string
extensionName?: string
}
export function isImageActive(
editor: Editor | null,
extensionName: string
): boolean {
if (!editor) return false
return editor.isActive(extensionName)
}
export function insertImage(
editor: Editor | null,
extensionName: string
): boolean {
if (!editor) return false
return editor
.chain()
.focus()
.insertContent({
type: extensionName,
})
.run()
}
export function useImageUploadButton(
editor: Editor | null,
extensionName: string = "imageUpload",
disabled: boolean = false
) {
const isActive = isImageActive(editor, extensionName)
const handleInsertImage = React.useCallback(() => {
if (disabled) return false
return insertImage(editor, extensionName)
}, [editor, extensionName, disabled])
return {
isActive,
handleInsertImage,
}
}
export const ImageUploadButton = React.forwardRef<
HTMLButtonElement,
ImageUploadButtonProps
>(
(
{
editor: providedEditor,
extensionName = "imageUpload",
text,
className = "",
disabled,
onClick,
children,
...buttonProps
},
ref
) => {
const editor = useTiptapEditor(providedEditor)
const { isActive, handleInsertImage } = useImageUploadButton(
editor,
extensionName,
disabled
)
const handleClick = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e)
if (!e.defaultPrevented && !disabled) {
handleInsertImage()
}
},
[onClick, disabled, handleInsertImage]
)
if (!editor || !editor.isEditable) {
return null
}
return (
<Button
ref={ref}
type="button"
className={className.trim()}
data-style="ghost"
data-active-state={isActive ? "on" : "off"}
role="button"
tabIndex={-1}
aria-label="Add image"
aria-pressed={isActive}
tooltip="Add image"
onClick={handleClick}
{...buttonProps}
>
{children || (
<>
<ImagePlusIcon className="tiptap-button-icon" />
{text && <span className="tiptap-button-text">{text}</span>}
</>
)}
</Button>
)
}
)
ImageUploadButton.displayName = "ImageUploadButton"
export default ImageUploadButton