Skip to content

Commit a69dc51

Browse files
hackerwinsclaude
andauthored
Add useRevisions hook to @yorkie-js/react for revision API (#1177)
Provide a useRevisions hook inside DocumentProvider that wraps the client's revision methods (createRevision, listRevisions, getRevision, restoreRevision) and auto-binds the current client and document so callers don't need to pass them manually. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 830a17b commit a69dc51

File tree

18 files changed

+1043
-2
lines changed

18 files changed

+1043
-2
lines changed

examples/react-revision/.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
VITE_YORKIE_API_ADDR='http://localhost:8080'
2+
VITE_YORKIE_API_KEY=''
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
VITE_YORKIE_API_ADDR='https://api.yorkie.dev'
2+
VITE_YORKIE_API_KEY='D6sd9E5ehtXBssmeRZ4QQP'
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { globalIgnores } from 'eslint/config';
2+
import prettierPlugin from 'eslint-plugin-prettier';
3+
import reactHooks from 'eslint-plugin-react-hooks';
4+
import reactRefresh from 'eslint-plugin-react-refresh';
5+
import baseConfig from '../../eslint.config.mjs';
6+
7+
const eslintConfig = [
8+
...baseConfig,
9+
reactHooks.configs['recommended-latest'],
10+
reactRefresh.configs.vite,
11+
{
12+
plugins: {
13+
prettier: prettierPlugin,
14+
},
15+
rules: {
16+
'prettier/prettier': 'error',
17+
'jsdoc/require-jsdoc': 'off',
18+
},
19+
},
20+
globalIgnores(['dist/*']),
21+
];
22+
23+
export default eslintConfig;

examples/react-revision/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Yorkie React Revision Example</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "react-revision",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc && vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"@yorkie-js/react": "workspace:*",
13+
"react": "^19.2.1",
14+
"react-dom": "^19.2.1"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^19.2.7",
18+
"@types/react-dom": "^19.2.3",
19+
"@vitejs/plugin-react": "^5.1.1",
20+
"eslint-plugin-react-hooks": "^7.0.1",
21+
"eslint-plugin-react-refresh": "^0.4.24",
22+
"typescript": "^5.9.3",
23+
"vite": "^7.2.6",
24+
"vite-tsconfig-paths": "^5.1.4"
25+
}
26+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
* {
2+
box-sizing: border-box;
3+
margin: 0;
4+
padding: 0;
5+
}
6+
7+
body {
8+
font-family:
9+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10+
background: #f5f5f5;
11+
color: #333;
12+
padding: 20px;
13+
}
14+
15+
.app {
16+
max-width: 1000px;
17+
margin: 0 auto;
18+
}
19+
20+
h1 {
21+
margin-bottom: 20px;
22+
font-size: 24px;
23+
}
24+
25+
h2 {
26+
font-size: 18px;
27+
margin-bottom: 12px;
28+
}
29+
30+
.loading,
31+
.error {
32+
text-align: center;
33+
padding: 40px;
34+
font-size: 16px;
35+
}
36+
37+
.error {
38+
color: #d32f2f;
39+
}
40+
41+
.editor-container {
42+
display: flex;
43+
gap: 20px;
44+
}
45+
46+
.editor-section {
47+
flex: 1;
48+
}
49+
50+
.editor {
51+
width: 100%;
52+
height: 400px;
53+
padding: 12px;
54+
border: 1px solid #ddd;
55+
border-radius: 6px;
56+
font-family: inherit;
57+
font-size: 14px;
58+
resize: vertical;
59+
outline: none;
60+
}
61+
62+
.editor:focus {
63+
border-color: #1976d2;
64+
}
65+
66+
.revision-section {
67+
width: 340px;
68+
flex-shrink: 0;
69+
}
70+
71+
.revision-panel {
72+
background: #fff;
73+
border: 1px solid #ddd;
74+
border-radius: 6px;
75+
padding: 16px;
76+
}
77+
78+
.create-revision {
79+
display: flex;
80+
flex-direction: column;
81+
gap: 8px;
82+
margin-bottom: 16px;
83+
padding-bottom: 16px;
84+
border-bottom: 1px solid #eee;
85+
}
86+
87+
.create-revision input {
88+
padding: 8px;
89+
border: 1px solid #ddd;
90+
border-radius: 4px;
91+
font-size: 13px;
92+
}
93+
94+
.create-revision button {
95+
padding: 8px 12px;
96+
background: #1976d2;
97+
color: #fff;
98+
border: none;
99+
border-radius: 4px;
100+
cursor: pointer;
101+
font-size: 13px;
102+
}
103+
104+
.create-revision button:disabled {
105+
background: #bbb;
106+
cursor: not-allowed;
107+
}
108+
109+
.revision-list {
110+
list-style: none;
111+
max-height: 400px;
112+
overflow-y: auto;
113+
}
114+
115+
.revision-item {
116+
display: flex;
117+
justify-content: space-between;
118+
align-items: flex-start;
119+
padding: 10px 0;
120+
border-bottom: 1px solid #eee;
121+
}
122+
123+
.revision-info strong {
124+
display: block;
125+
font-size: 14px;
126+
}
127+
128+
.revision-info p {
129+
font-size: 12px;
130+
color: #666;
131+
margin-top: 2px;
132+
}
133+
134+
.revision-info small {
135+
font-size: 11px;
136+
color: #999;
137+
}
138+
139+
.revision-actions {
140+
display: flex;
141+
gap: 4px;
142+
flex-shrink: 0;
143+
}
144+
145+
.revision-actions button {
146+
padding: 4px 8px;
147+
border: 1px solid #ddd;
148+
border-radius: 4px;
149+
background: #fff;
150+
cursor: pointer;
151+
font-size: 12px;
152+
}
153+
154+
.revision-actions button:hover {
155+
background: #f0f0f0;
156+
}
157+
158+
.revision-empty {
159+
text-align: center;
160+
padding: 20px;
161+
color: #999;
162+
font-size: 13px;
163+
}
164+
165+
.preview-overlay {
166+
position: fixed;
167+
top: 0;
168+
left: 0;
169+
right: 0;
170+
bottom: 0;
171+
background: rgba(0, 0, 0, 0.5);
172+
display: flex;
173+
align-items: center;
174+
justify-content: center;
175+
z-index: 100;
176+
}
177+
178+
.preview-modal {
179+
background: #fff;
180+
border-radius: 8px;
181+
padding: 20px;
182+
max-width: 600px;
183+
width: 90%;
184+
max-height: 80vh;
185+
overflow-y: auto;
186+
}
187+
188+
.preview-modal h3 {
189+
margin-bottom: 12px;
190+
}
191+
192+
.preview-modal pre {
193+
background: #f5f5f5;
194+
padding: 12px;
195+
border-radius: 4px;
196+
font-size: 13px;
197+
overflow-x: auto;
198+
margin-bottom: 12px;
199+
}
200+
201+
.preview-modal button {
202+
padding: 8px 16px;
203+
background: #666;
204+
color: #fff;
205+
border: none;
206+
border-radius: 4px;
207+
cursor: pointer;
208+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { YorkieProvider, DocumentProvider, useDocument } from '@yorkie-js/react';
2+
import RevisionPanel from './RevisionPanel';
3+
import './App.css';
4+
5+
const DOC_KEY = `react-revision-${new Date().toISOString().slice(0, 10)}`;
6+
7+
interface DocType {
8+
content: string;
9+
}
10+
11+
function NoteEditor() {
12+
const { root, update, loading, error } = useDocument<DocType>();
13+
14+
if (loading) return <div className="loading">Loading...</div>;
15+
if (error) return <div className="error">Error: {error.message}</div>;
16+
17+
return (
18+
<div className="app">
19+
<h1>Note with Revisions</h1>
20+
<div className="editor-container">
21+
<div className="editor-section">
22+
<h2>Editor</h2>
23+
<textarea
24+
className="editor"
25+
value={root.content}
26+
onChange={(e) =>
27+
update((root) => {
28+
root.content = e.target.value;
29+
})
30+
}
31+
placeholder="Start typing your note..."
32+
/>
33+
</div>
34+
<div className="revision-section">
35+
<RevisionPanel />
36+
</div>
37+
</div>
38+
</div>
39+
);
40+
}
41+
42+
export default function App() {
43+
return (
44+
<YorkieProvider
45+
apiKey={import.meta.env.VITE_YORKIE_API_KEY}
46+
rpcAddr={import.meta.env.VITE_YORKIE_API_ADDR}
47+
>
48+
<DocumentProvider docKey={DOC_KEY} initialRoot={{ content: '' }}>
49+
<NoteEditor />
50+
</DocumentProvider>
51+
</YorkieProvider>
52+
);
53+
}

0 commit comments

Comments
 (0)