This commit is contained in:
呈祥
2026-01-06 19:49:07 +08:00
parent 210d948353
commit 68ad8b3726
6 changed files with 233 additions and 571 deletions
+100 -1
View File
@@ -1,7 +1,106 @@
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import "./App.css";
import { Client } from "./ot/client";
import { generateOperations } from "./ot/helpers";
import { getDemoServerInstance } from "./ot/server";
function Editor({ name }: { name: string }) {
const clientRef = useRef<Client | null>(null);
const [doc, setDoc] = useState<string>("");
const prevDocRef = useRef<string>("");
const [enableSync, setEnableSync] = useState<boolean>(true);
// 初始化client和文档状态
useLayoutEffect(() => {
if (!clientRef.current) {
clientRef.current = new Client();
clientRef.current.name = name;
const initialDoc = clientRef.current.document || "";
prevDocRef.current = initialDoc;
setDoc(initialDoc);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!clientRef.current) return;
// 定期同步client的文档状态到UI
const timer = setInterval(() => {
const currentDoc = clientRef.current!.document || "";
if (currentDoc !== prevDocRef.current) {
setDoc(currentDoc);
prevDocRef.current = currentDoc;
}
}, 100);
return () => clearInterval(timer);
}, []);
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!clientRef.current) return;
const newValue = e.target.value;
// 使用client当前文档状态作为旧值
const oldValue = clientRef.current.document || "";
if (newValue !== oldValue) {
// 生成操作集合
const operations = generateOperations(oldValue, newValue);
// 按顺序应用操作
operations.forEach((op) => {
clientRef.current!.applyClient(op);
});
}
};
const handleToggleSync = () => {
const newValue = !enableSync;
setEnableSync(newValue);
if (clientRef.current) {
clientRef.current.setEnableSync(newValue);
}
};
return (
<div style={{ border: "1px solid #ccc", padding: 10 }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 10,
}}
>
<p style={{ margin: 0 }}>Document</p>
<label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
<input type="checkbox" checked={enableSync} onChange={handleToggleSync} />
<span></span>
</label>
</div>
<textarea value={doc} onChange={handleChange} rows={5} />
</div>
);
}
function App() {
return <div>he</div>;
const [serverDoc, setServerDoc] = useState<string>("");
useEffect(() => {
const timer = setInterval(() => {
setServerDoc(getDemoServerInstance().document || "");
}, 100);
return () => clearInterval(timer);
}, []);
return (
<div>
<div style={{ border: "1px solid #ccc", padding: 10 }}>
<p>Server Document:</p>
<p>{serverDoc}</p>
</div>
<div style={{ display: "flex", gap: 10, marginTop: 20 }}>
<Editor name="A" />
<Editor name="B" />
</div>
</div>
);
}
export default App;