forked from 77media/video-flow
172 lines
5.4 KiB
TypeScript
172 lines
5.4 KiB
TypeScript
import Placeholder from "@tiptap/extension-placeholder";
|
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
import StarterKit from "@tiptap/starter-kit";
|
|
import { useEffect } from "react";
|
|
import { HighlightTextExtension } from "../ui/main-editor/HighlightText";
|
|
|
|
/**
|
|
* 高亮编辑器组件
|
|
* 使用 Tiptap 实现高亮和文本编辑功能
|
|
* 让 文本中的<type>xxxx</type>标签高亮
|
|
*/
|
|
export const HighlightEditor = ({
|
|
content,
|
|
onContentChange,
|
|
type,
|
|
placeholder,
|
|
cursorPosition,
|
|
onCursorPositionChange,
|
|
}: {
|
|
/** 内容 */
|
|
content: string;
|
|
/** 内容变化回调 */
|
|
onContentChange: (content: string) => void;
|
|
/** 标签类型*/
|
|
type: string;
|
|
/**提示语 */
|
|
placeholder: string;
|
|
/** 光标位置 */
|
|
cursorPosition?: number;
|
|
/** 光标位置变化回调 */
|
|
onCursorPositionChange?: (position: number) => void;
|
|
}) => {
|
|
console.log(44444);
|
|
|
|
const editor = useEditor({
|
|
extensions: [
|
|
StarterKit,
|
|
HighlightTextExtension,
|
|
Placeholder.configure({
|
|
placeholder,
|
|
emptyEditorClass: "is-editor-empty",
|
|
}),
|
|
],
|
|
content: "",
|
|
onUpdate: ({ editor }) => {
|
|
const textContent = editor.getText();
|
|
if (!textContent.trim()) {
|
|
onContentChange("");
|
|
return;
|
|
}
|
|
|
|
// 获取 HTML 内容并转换 highlight-text 标签
|
|
const htmlContent = editor.getHTML();
|
|
const convertedContent = htmlContent.replace(
|
|
/<highlight-text[^>]*type="([^"]*)"[^>]*text="([^"]*)"[^>]*>([^<]*)<\/highlight-text>/g,
|
|
'<$1>$2</$1>'
|
|
);
|
|
|
|
console.log('convertedContent:::', convertedContent);
|
|
|
|
// 保存当前光标位置
|
|
const { from } = editor.state.selection;
|
|
onCursorPositionChange?.(from);
|
|
|
|
// 传递转换后的内容
|
|
onContentChange(convertedContent);
|
|
},
|
|
editorProps: {
|
|
handleKeyDown: (view, event) => {
|
|
const { from, to } = view.state.selection;
|
|
const doc = view.state.doc;
|
|
// 检查光标前后是否有标签
|
|
const textBefore =
|
|
from > 0 ? doc.textBetween(Math.max(0, from - 50), from) : "";
|
|
const textAfter =
|
|
to < doc.content.size
|
|
? doc.textBetween(to, Math.min(doc.content.size, to + 50))
|
|
: "";
|
|
const beforeMatch = textBefore.match(new RegExp(`<${type}[^>]*>[^<]*$`));
|
|
const afterMatch = textAfter.match(new RegExp(`^[^>]*<\\/${type}>`));
|
|
|
|
// 只允许删除操作)
|
|
if (beforeMatch || afterMatch) {
|
|
if (event.key !== "Backspace" && event.key !== "Delete") {
|
|
event.preventDefault();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
},
|
|
immediatelyRender: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (editor) {
|
|
if (!content || content.trim() === "") {
|
|
editor.commands.clearContent(true);
|
|
return;
|
|
}
|
|
// 将带标签的内容转换为高亮显示(支持新的标签格式)
|
|
const htmlContent = content.replace(
|
|
new RegExp(`<${type}[^>]*>([^<]+)<\/${type}>`, "g"),
|
|
'<highlight-text type="' + type + '" text="$1" color="blue">$1</highlight-text>'
|
|
);
|
|
console.log('111111', 111111)
|
|
editor.commands.setContent(htmlContent, { emitUpdate: false });
|
|
|
|
// 恢复光标位置
|
|
if (cursorPosition && cursorPosition > 0) {
|
|
// 确保光标位置不超出文档范围
|
|
const docSize = editor.state.doc.content.size;
|
|
const safePosition = Math.min(cursorPosition, docSize);
|
|
if (safePosition > 0) {
|
|
editor.commands.setTextSelection(safePosition);
|
|
}
|
|
}
|
|
}
|
|
}, [content, editor, cursorPosition]);
|
|
|
|
return (
|
|
<div className="flex-1 min-w-0 relative pr-20">
|
|
<style jsx>{`
|
|
.${type}-name-highlight {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white !important;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-weight: 500;
|
|
margin: 0 2px;
|
|
user-select: none;
|
|
cursor: default;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* 移除 Tiptap 编辑器的默认焦点样式 */
|
|
.ProseMirror:focus {
|
|
outline: none !important;
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
.ProseMirror:focus-visible {
|
|
outline: none !important;
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
/* 移除编辑器容器的焦点样式 */
|
|
.ProseMirror-focused {
|
|
outline: none !important;
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
/* 确保编辑器内容区域没有边框和轮廓 */
|
|
.ProseMirror {
|
|
outline: none !important;
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
}
|
|
`}</style>
|
|
<EditorContent
|
|
editor={editor}
|
|
className="w-full bg-transparent border-none outline-none resize-none text-white placeholder:text-white/40 text-sm leading-relaxed min-h-[60px] focus:outline-none focus:ring-0 focus:border-0"
|
|
style={{ outline: "none", border: "none", boxShadow: "none" }}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|