125 lines
3.5 KiB
TypeScript

import React, { useState } from 'react';
import { EditorContent, useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { motion } from "framer-motion";
import { CharacterTokenExtension } from './CharacterToken';
import { ShotTitle } from './ShotTitle';
import { ReadonlyText } from './ReadonlyText';
const initialContent = {
type: 'doc',
content: [
{
type: 'shotTitle',
attrs: { title: `分镜1` },
},
{
type: 'paragraph',
content: [
{ type: 'characterToken', attrs: { name: '张三', gender: '男', age: '28', avatar: 'https://i.pravatar.cc/40?u=z3' }},
{ type: 'text', text: ' 从门口走来,皱着眉头说:“你怎么还在这里?”' }
]
},
{
type: 'shotTitle',
attrs: { title: `分镜2` },
},
{
type: 'paragraph',
content: [
{ type: 'characterToken', attrs: { name: '李四', gender: '女', age: '26', avatar: 'https://i.pravatar.cc/40?u=l4' }},
{ type: 'text', text: ' 微微低头,没有说话。' }
]
}
]
};
interface ShotEditorProps {
onAddSegment?: () => void;
}
const ShotEditor = React.forwardRef<{ addSegment: () => void }, ShotEditorProps>(function ShotEditor({ onAddSegment }, ref) {
const [segments, setSegments] = useState(initialContent.content);
const editor = useEditor({
extensions: [
StarterKit,
CharacterTokenExtension,
ShotTitle,
ReadonlyText,
],
content: { type: 'doc', content: segments },
editorProps: {
attributes: {
class: 'prose prose-invert max-w-none min-h-[150px] focus:outline-none'
}
},
immediatelyRender: false,
onCreate: ({ editor }) => {
editor.setOptions({ editable: true })
},
})
const addSegment = () => {
if (!editor) return;
// 自动编号(获取已有 shotTitle 节点数量)
const doc = editor.state.doc;
let shotCount = 0;
doc.descendants((node) => {
if (node.type.name === 'paragraph') {
shotCount++;
}
});
editor.chain().focus('end').insertContent([
{
type: 'shotTitle',
attrs: { title: `分镜${shotCount + 1}` },
},
{
type: 'paragraph',
content: [
{ type: 'text', text: '镜头描述' }
]
}
])
.focus('end') // 聚焦到文档末尾
.run();
// 调用外部传入的回调函数
onAddSegment?.();
};
// 暴露 addSegment 方法给父组件
React.useImperativeHandle(ref, () => ({
addSegment
}));
if (!editor) {
return null
}
return (
<div className="w-full max-w-3xl mx-auto relative p-[0.5rem] pb-[2.5rem] border border-white/10 rounded-[0.5rem]">
<EditorContent editor={editor} />
{/* <motion.button
onClick={addSegment}
className="group absolute bottom-[0.5rem] h-8 rounded-full bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 flex items-center justify-center overflow-hidden"
initial={{ width: "2rem" }}
whileHover={{
width: "8rem",
transition: { duration: 0.3, ease: "easeInOut" }
}}
>
<motion.div
className="flex items-center justify-center space-x-1 px-2 h-full">
<span className="text-lg">+</span>
<span className="text-sm group-hover:opacity-100 opacity-0 transition-all duration-500 w-0 group-hover:w-auto">新增分镜</span>
</motion.div>
</motion.button> */}
</div>
)
});
export default ShotEditor;