73 lines
1.8 KiB
TypeScript

import { Node, mergeAttributes } from '@tiptap/core'
import { ReactNodeViewRenderer, NodeViewWrapper, ReactNodeViewProps } from '@tiptap/react'
import { motion, AnimatePresence } from 'framer-motion'
import { useState } from 'react'
import { Check } from 'lucide-react'
interface HighlightTextAttributes {
type: string;
text: string;
color: string;
}
interface HighlightTextOptions {
type?: string;
text: string;
color: string;
}
export function HighlightText(props: ReactNodeViewProps) {
const { text: initialText, color } = props.node.attrs as HighlightTextAttributes
const [text, setText] = useState(initialText)
const handleInput = (e: React.FormEvent<HTMLSpanElement>) => {
const newText = e.currentTarget.textContent || ''
setText(newText)
// 通知Tiptap更新内容
props.updateAttributes({
text: newText
})
}
return (
<NodeViewWrapper
as="span"
data-alt="highlight-text"
contentEditable={true}
suppressContentEditableWarning={true}
onInput={handleInput}
className={`relative inline text-${color}-400 hover:text-${color}-300 transition-colors duration-200`}
>
{text}
{/* 暂时空着 为后续可视化文本预留 */}
</NodeViewWrapper>
)
}
export const HighlightTextExtension = Node.create<HighlightTextOptions>({
name: 'highlightText',
group: 'inline',
inline: true,
atom: false,
addAttributes() {
return {
type: { default: null },
text: { default: '' },
color: { default: 'blue' },
};
},
parseHTML() {
return [{ tag: 'highlight-text' }];
},
renderHTML({ HTMLAttributes }) {
return ['highlight-text', mergeAttributes(HTMLAttributes)];
},
addNodeView() {
return ReactNodeViewRenderer(HighlightText);
},
});