@stratasync/react 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/dist/context.d.ts +21 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +27 -0
- package/dist/context.js.map +1 -0
- package/dist/hooks/use-connection-state.d.ts +55 -0
- package/dist/hooks/use-connection-state.d.ts.map +1 -0
- package/dist/hooks/use-connection-state.js +90 -0
- package/dist/hooks/use-connection-state.js.map +1 -0
- package/dist/hooks/use-model.d.ts +22 -0
- package/dist/hooks/use-model.d.ts.map +1 -0
- package/dist/hooks/use-model.js +162 -0
- package/dist/hooks/use-model.js.map +1 -0
- package/dist/hooks/use-query.d.ts +35 -0
- package/dist/hooks/use-query.d.ts.map +1 -0
- package/dist/hooks/use-query.js +302 -0
- package/dist/hooks/use-query.js.map +1 -0
- package/dist/hooks/use-sync-client.d.ts +25 -0
- package/dist/hooks/use-sync-client.d.ts.map +1 -0
- package/dist/hooks/use-sync-client.js +49 -0
- package/dist/hooks/use-sync-client.js.map +1 -0
- package/dist/hooks/use-yjs-document.d.ts +108 -0
- package/dist/hooks/use-yjs-document.d.ts.map +1 -0
- package/dist/hooks/use-yjs-document.js +202 -0
- package/dist/hooks/use-yjs-document.js.map +1 -0
- package/dist/hooks/use-yjs-presence.d.ts +62 -0
- package/dist/hooks/use-yjs-presence.d.ts.map +1 -0
- package/dist/hooks/use-yjs-presence.js +230 -0
- package/dist/hooks/use-yjs-presence.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +117 -0
- package/dist/provider.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { useSyncClientInstance, useSyncReady } from "./use-sync-client.js";
|
|
3
|
+
const captureSnapshots = (items) => items.map((item) => {
|
|
4
|
+
const record = item;
|
|
5
|
+
return { id: record.id, updatedAt: record.updatedAt };
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Compares new query result items against previously-captured snapshots.
|
|
9
|
+
* Returns true if every item matches its snapshot by id + updatedAt.
|
|
10
|
+
*
|
|
11
|
+
* Unlike a direct reference comparison, this detects in-place mutations
|
|
12
|
+
* because the snapshot stores *copied* values from the time data was last
|
|
13
|
+
* committed to React state.
|
|
14
|
+
*/
|
|
15
|
+
const isQueryResultEqual = (snapshots, next) => {
|
|
16
|
+
if (snapshots.length !== next.length) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
for (let i = 0; i < snapshots.length; i += 1) {
|
|
20
|
+
const s = snapshots[i];
|
|
21
|
+
const n = next[i];
|
|
22
|
+
if (s?.id !== n.id) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (s?.updatedAt !== n.updatedAt) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Default empty state for a query that hasn't loaded data yet.
|
|
33
|
+
*/
|
|
34
|
+
const emptyQueryState = (isLoading) => ({
|
|
35
|
+
data: [],
|
|
36
|
+
hasMore: false,
|
|
37
|
+
isLoading,
|
|
38
|
+
totalCount: undefined,
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Cast UseQueryOptions<T> to QueryOptions compatible with the identity map's
|
|
42
|
+
* `T & Record<string, unknown>` shape.
|
|
43
|
+
*/
|
|
44
|
+
const buildSyncQueryOptions = (opts) => ({
|
|
45
|
+
includeArchived: opts.includeArchived,
|
|
46
|
+
limit: opts.limit,
|
|
47
|
+
offset: opts.offset,
|
|
48
|
+
orderBy: opts.orderBy,
|
|
49
|
+
where: opts.where,
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Synchronously query from a raw Map (avoids flash of empty state)
|
|
53
|
+
*/
|
|
54
|
+
const querySyncFromMap = (map, options = {}) => {
|
|
55
|
+
let results = [...map.values()];
|
|
56
|
+
if (options.where) {
|
|
57
|
+
results = results.filter(options.where);
|
|
58
|
+
}
|
|
59
|
+
if (!options.includeArchived) {
|
|
60
|
+
results = results.filter((item) => !item.archivedAt);
|
|
61
|
+
}
|
|
62
|
+
const totalCount = results.length;
|
|
63
|
+
if (options.orderBy) {
|
|
64
|
+
results = results.toSorted(options.orderBy);
|
|
65
|
+
}
|
|
66
|
+
if (options.offset && options.offset > 0) {
|
|
67
|
+
results = results.slice(options.offset);
|
|
68
|
+
}
|
|
69
|
+
let hasMore = false;
|
|
70
|
+
if (options.limit && options.limit > 0) {
|
|
71
|
+
hasMore = results.length > options.limit;
|
|
72
|
+
results = results.slice(0, options.limit);
|
|
73
|
+
}
|
|
74
|
+
return { data: results, hasMore, totalCount };
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Hook to query models with filtering, sorting, and pagination
|
|
78
|
+
*
|
|
79
|
+
* @param modelName - Name of the model to query
|
|
80
|
+
* @param options - Query options including filters, sorting, and pagination
|
|
81
|
+
* @returns UseQueryResult with data array, loading state, and metadata
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```tsx
|
|
85
|
+
* function TaskList({ projectId }: { projectId: string }) {
|
|
86
|
+
* const { data: tasks, isLoading, hasMore } = useQuery<Task>('Task', {
|
|
87
|
+
* where: (task) => task.projectId === projectId,
|
|
88
|
+
* orderBy: (a, b) => a.createdAt - b.createdAt,
|
|
89
|
+
* limit: 20,
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* if (isLoading) return <Spinner />;
|
|
93
|
+
*
|
|
94
|
+
* return (
|
|
95
|
+
* <ul>
|
|
96
|
+
* {tasks.map((task) => (
|
|
97
|
+
* <li key={task.id}>{task.title}</li>
|
|
98
|
+
* ))}
|
|
99
|
+
* </ul>
|
|
100
|
+
* );
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export const useQuery = (modelName, options = {}) => {
|
|
105
|
+
const client = useSyncClientInstance();
|
|
106
|
+
const isReady = useSyncReady();
|
|
107
|
+
// Compute initial state from identity map (only runs on mount via lazy useState)
|
|
108
|
+
const computeState = () => {
|
|
109
|
+
if (options.skip) {
|
|
110
|
+
return emptyQueryState(false);
|
|
111
|
+
}
|
|
112
|
+
if (!isReady) {
|
|
113
|
+
return emptyQueryState(true);
|
|
114
|
+
}
|
|
115
|
+
const map = client.getIdentityMap(modelName);
|
|
116
|
+
if (map.size === 0) {
|
|
117
|
+
return emptyQueryState(true);
|
|
118
|
+
}
|
|
119
|
+
const result = querySyncFromMap(map, buildSyncQueryOptions(options));
|
|
120
|
+
return {
|
|
121
|
+
data: result.data,
|
|
122
|
+
hasMore: result.hasMore,
|
|
123
|
+
isLoading: false,
|
|
124
|
+
totalCount: result.totalCount,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
// Lazy initializers — only run on mount, preventing MobX tracking on re-renders
|
|
128
|
+
const initialRef = useRef(null);
|
|
129
|
+
if (initialRef.current === null) {
|
|
130
|
+
initialRef.current = computeState();
|
|
131
|
+
}
|
|
132
|
+
const initial = initialRef.current;
|
|
133
|
+
const [data, setData] = useState(initial.data);
|
|
134
|
+
const [isLoading, setIsLoading] = useState(initial.isLoading);
|
|
135
|
+
const [error, setError] = useState(null);
|
|
136
|
+
const [totalCount, setTotalCount] = useState(initial.totalCount);
|
|
137
|
+
const [hasMore, setHasMore] = useState(initial.hasMore);
|
|
138
|
+
// Use ref to track options to avoid infinite loops
|
|
139
|
+
const optionsRef = useRef(options);
|
|
140
|
+
optionsRef.current = options;
|
|
141
|
+
// Track current data for structural equality checks (avoids unnecessary re-renders)
|
|
142
|
+
const dataRef = useRef(initial.data);
|
|
143
|
+
// Snapshot of id+updatedAt per item — detects in-place identity-map mutations
|
|
144
|
+
const snapshotsRef = useRef(captureSnapshots(initial.data));
|
|
145
|
+
// Track if we have data to avoid setting loading state when refreshing cached data
|
|
146
|
+
const hasDataRef = useRef(initial.data.length > 0);
|
|
147
|
+
// Ref mirrors for metadata state — only call setters when values actually change
|
|
148
|
+
const totalCountRef = useRef(initial.totalCount);
|
|
149
|
+
const hasMoreRef = useRef(initial.hasMore);
|
|
150
|
+
const isLoadingRef = useRef(initial.isLoading);
|
|
151
|
+
const errorRef = useRef(null);
|
|
152
|
+
// Microtask debounce flag — coalesces rapid modelChange events into one refresh
|
|
153
|
+
const pendingRefreshRef = useRef(false);
|
|
154
|
+
const requestVersionRef = useRef(0);
|
|
155
|
+
/**
|
|
156
|
+
* Apply a query result to React state. Only calls setters when values
|
|
157
|
+
* actually changed, preventing unnecessary re-renders.
|
|
158
|
+
*/
|
|
159
|
+
const applyResult = useCallback((resultData, resultTotalCount, resultHasMore, applyOptions = {}) => {
|
|
160
|
+
if (applyOptions.forceDataUpdate ||
|
|
161
|
+
!isQueryResultEqual(snapshotsRef.current, resultData)) {
|
|
162
|
+
snapshotsRef.current = captureSnapshots(resultData);
|
|
163
|
+
dataRef.current = resultData;
|
|
164
|
+
setData(resultData);
|
|
165
|
+
}
|
|
166
|
+
if (resultTotalCount !== totalCountRef.current) {
|
|
167
|
+
totalCountRef.current = resultTotalCount;
|
|
168
|
+
setTotalCount(resultTotalCount);
|
|
169
|
+
}
|
|
170
|
+
if (resultHasMore !== hasMoreRef.current) {
|
|
171
|
+
hasMoreRef.current = resultHasMore;
|
|
172
|
+
setHasMore(resultHasMore);
|
|
173
|
+
}
|
|
174
|
+
hasDataRef.current = resultData.length > 0;
|
|
175
|
+
if (isLoadingRef.current !== false) {
|
|
176
|
+
isLoadingRef.current = false;
|
|
177
|
+
setIsLoading(false);
|
|
178
|
+
}
|
|
179
|
+
if (errorRef.current !== null) {
|
|
180
|
+
errorRef.current = null;
|
|
181
|
+
setError(null);
|
|
182
|
+
}
|
|
183
|
+
}, []);
|
|
184
|
+
/** Clear data and stop loading (used when query is skipped). */
|
|
185
|
+
const clearSkipped = useCallback(() => {
|
|
186
|
+
if (dataRef.current.length > 0) {
|
|
187
|
+
dataRef.current = [];
|
|
188
|
+
snapshotsRef.current = [];
|
|
189
|
+
setData([]);
|
|
190
|
+
}
|
|
191
|
+
if (isLoadingRef.current !== false) {
|
|
192
|
+
isLoadingRef.current = false;
|
|
193
|
+
setIsLoading(false);
|
|
194
|
+
}
|
|
195
|
+
}, []);
|
|
196
|
+
const executeQuery = useCallback(async () => {
|
|
197
|
+
if (optionsRef.current.skip) {
|
|
198
|
+
requestVersionRef.current += 1;
|
|
199
|
+
clearSkipped();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
requestVersionRef.current += 1;
|
|
203
|
+
const requestVersion = requestVersionRef.current;
|
|
204
|
+
// Only show loading if we don't already have cached data
|
|
205
|
+
// This prevents the flash of empty state when refreshing
|
|
206
|
+
if (!hasDataRef.current) {
|
|
207
|
+
isLoadingRef.current = true;
|
|
208
|
+
setIsLoading(true);
|
|
209
|
+
}
|
|
210
|
+
if (errorRef.current !== null) {
|
|
211
|
+
errorRef.current = null;
|
|
212
|
+
setError(null);
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const queryOptions = {
|
|
216
|
+
includeArchived: optionsRef.current.includeArchived,
|
|
217
|
+
limit: optionsRef.current.limit,
|
|
218
|
+
offset: optionsRef.current.offset,
|
|
219
|
+
orderBy: optionsRef.current.orderBy,
|
|
220
|
+
where: optionsRef.current.where,
|
|
221
|
+
};
|
|
222
|
+
const result = await client.query(modelName, queryOptions);
|
|
223
|
+
if (requestVersion !== requestVersionRef.current) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
applyResult(result.data, result.totalCount, result.hasMore);
|
|
227
|
+
}
|
|
228
|
+
catch (queryError) {
|
|
229
|
+
if (requestVersion !== requestVersionRef.current) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const newError = queryError instanceof Error
|
|
233
|
+
? queryError
|
|
234
|
+
: new Error(String(queryError));
|
|
235
|
+
errorRef.current = newError;
|
|
236
|
+
setError(newError);
|
|
237
|
+
// Preserve stale data on refresh errors to avoid UI flash.
|
|
238
|
+
hasDataRef.current = dataRef.current.length > 0;
|
|
239
|
+
if (isLoadingRef.current !== false) {
|
|
240
|
+
isLoadingRef.current = false;
|
|
241
|
+
setIsLoading(false);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}, [client, modelName, applyResult, clearSkipped]);
|
|
245
|
+
// Synchronous refresh — reads identity map and updates React state immediately
|
|
246
|
+
const refreshSync = useCallback(() => {
|
|
247
|
+
if (optionsRef.current.skip) {
|
|
248
|
+
clearSkipped();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const map = client.getIdentityMap(modelName);
|
|
252
|
+
const result = querySyncFromMap(map, buildSyncQueryOptions(optionsRef.current));
|
|
253
|
+
applyResult(result.data, result.totalCount, result.hasMore);
|
|
254
|
+
}, [client, modelName, applyResult, clearSkipped]);
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
if (isReady && !options.skip) {
|
|
257
|
+
executeQuery();
|
|
258
|
+
}
|
|
259
|
+
else if (options.skip) {
|
|
260
|
+
requestVersionRef.current += 1;
|
|
261
|
+
clearSkipped();
|
|
262
|
+
}
|
|
263
|
+
}, [isReady, options.skip, executeQuery, clearSkipped]);
|
|
264
|
+
useEffect(() => () => {
|
|
265
|
+
requestVersionRef.current += 1;
|
|
266
|
+
}, []);
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
if (!isReady || options.skip) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const unsubscribe = client.onEvent((event) => {
|
|
272
|
+
// Coalesce rapid modelChange events (e.g. a delta packet with many
|
|
273
|
+
// actions for the same model type) into a single refreshSync call.
|
|
274
|
+
if (event.type === "modelChange" &&
|
|
275
|
+
event.modelName === modelName &&
|
|
276
|
+
!pendingRefreshRef.current) {
|
|
277
|
+
pendingRefreshRef.current = true;
|
|
278
|
+
queueMicrotask(() => {
|
|
279
|
+
pendingRefreshRef.current = false;
|
|
280
|
+
refreshSync();
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return () => {
|
|
285
|
+
unsubscribe();
|
|
286
|
+
pendingRefreshRef.current = false;
|
|
287
|
+
};
|
|
288
|
+
}, [client, modelName, isReady, options.skip, refreshSync]);
|
|
289
|
+
return {
|
|
290
|
+
data,
|
|
291
|
+
error,
|
|
292
|
+
hasMore,
|
|
293
|
+
isLoading,
|
|
294
|
+
refresh: executeQuery,
|
|
295
|
+
totalCount,
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Hook to query all models of a type
|
|
300
|
+
*/
|
|
301
|
+
export const useQueryAll = (modelName, options = {}) => useQuery(modelName, options);
|
|
302
|
+
//# sourceMappingURL=use-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-query.js","sourceRoot":"","sources":["../../src/hooks/use-query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SyncContextValue, SyncStatusContextValue } from "../types.js";
|
|
2
|
+
export declare const useSyncStatusValue: () => SyncStatusContextValue;
|
|
3
|
+
export declare const useSyncBacklogValue: () => number;
|
|
4
|
+
/**
|
|
5
|
+
* Hook to access the sync client from context
|
|
6
|
+
* @throws Error if used outside of SyncProvider
|
|
7
|
+
*/
|
|
8
|
+
export declare const useSyncClient: () => SyncContextValue;
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access just the sync client instance
|
|
11
|
+
*/
|
|
12
|
+
export declare const useSyncClientInstance: () => SyncContextValue["client"];
|
|
13
|
+
/**
|
|
14
|
+
* Hook to check if the sync client is ready
|
|
15
|
+
*/
|
|
16
|
+
export declare const useSyncReady: () => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Hook to get the current sync state
|
|
19
|
+
*/
|
|
20
|
+
export declare const useSyncState: () => SyncContextValue["state"];
|
|
21
|
+
/**
|
|
22
|
+
* Hook to get the latest sync error.
|
|
23
|
+
*/
|
|
24
|
+
export declare const useSyncError: () => Error | null;
|
|
25
|
+
//# sourceMappingURL=use-sync-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-sync-client.d.ts","sourceRoot":"","sources":["../../src/hooks/use-sync-client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAY5E,eAAO,MAAM,kBAAkB,QAAO,sBAMrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAAO,MAItC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,QAAO,gBAMhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAO,gBAAgB,CAAC,QAAQ,CACzC,CAAC;AAE1B;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,OAAuC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,gBAAgB,CAAC,OAAO,CAC5B,CAAC;AAE7B;;GAEG;AACH,eAAO,MAAM,YAAY,QAAO,KAAK,GAAG,IAAkC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { SyncBacklogContext, SyncClientContext, SyncStatusContext, } from "../context.js";
|
|
3
|
+
const SYNC_PROVIDER_ERROR = "useSyncClient must be used within a SyncProvider";
|
|
4
|
+
const useRequiredSyncClient = () => {
|
|
5
|
+
const client = useContext(SyncClientContext);
|
|
6
|
+
if (!client) {
|
|
7
|
+
throw new Error(SYNC_PROVIDER_ERROR);
|
|
8
|
+
}
|
|
9
|
+
return client;
|
|
10
|
+
};
|
|
11
|
+
export const useSyncStatusValue = () => {
|
|
12
|
+
const status = useContext(SyncStatusContext);
|
|
13
|
+
if (!status) {
|
|
14
|
+
throw new Error(SYNC_PROVIDER_ERROR);
|
|
15
|
+
}
|
|
16
|
+
return status;
|
|
17
|
+
};
|
|
18
|
+
export const useSyncBacklogValue = () => {
|
|
19
|
+
// Ensure hooks still throw a clear provider error when used outside context.
|
|
20
|
+
useRequiredSyncClient();
|
|
21
|
+
return useContext(SyncBacklogContext);
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Hook to access the sync client from context
|
|
25
|
+
* @throws Error if used outside of SyncProvider
|
|
26
|
+
*/
|
|
27
|
+
export const useSyncClient = () => {
|
|
28
|
+
const client = useRequiredSyncClient();
|
|
29
|
+
const status = useSyncStatusValue();
|
|
30
|
+
const backlog = useSyncBacklogValue();
|
|
31
|
+
return { backlog, client, ...status };
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Hook to access just the sync client instance
|
|
35
|
+
*/
|
|
36
|
+
export const useSyncClientInstance = () => useRequiredSyncClient();
|
|
37
|
+
/**
|
|
38
|
+
* Hook to check if the sync client is ready
|
|
39
|
+
*/
|
|
40
|
+
export const useSyncReady = () => useSyncStatusValue().isReady;
|
|
41
|
+
/**
|
|
42
|
+
* Hook to get the current sync state
|
|
43
|
+
*/
|
|
44
|
+
export const useSyncState = () => useSyncStatusValue().state;
|
|
45
|
+
/**
|
|
46
|
+
* Hook to get the latest sync error.
|
|
47
|
+
*/
|
|
48
|
+
export const useSyncError = () => useSyncStatusValue().error;
|
|
49
|
+
//# sourceMappingURL=use-sync-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-sync-client.js","sourceRoot":"","sources":["../../src/hooks/use-sync-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAE/E,MAAM,qBAAqB,GAAG,GAA+B,EAAE;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAA2B,EAAE;IAC7D,MAAM,MAAM,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC9C,6EAA6E;IAC7E,qBAAqB,EAAE,CAAC;IACxB,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAqB,EAAE;IAClD,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAC;IAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;AACxC,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAA+B,EAAE,CACpE,qBAAqB,EAAE,CAAC;AAE1B;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,kBAAkB,EAAE,CAAC,OAAO,CAAC;AAExE;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAA8B,EAAE,CAC1D,kBAAkB,EAAE,CAAC,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,GAAiB,EAAE,CAAC,kBAAkB,EAAE,CAAC,KAAK,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for Yjs collaborative document editing.
|
|
3
|
+
*/
|
|
4
|
+
import type { Doc as YDoc } from "yjs";
|
|
5
|
+
/**
|
|
6
|
+
* Document key identifying a collaborative field.
|
|
7
|
+
*/
|
|
8
|
+
export interface DocumentKey {
|
|
9
|
+
entityType: string;
|
|
10
|
+
entityId: string;
|
|
11
|
+
fieldName: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Connection state for Yjs document.
|
|
15
|
+
*/
|
|
16
|
+
export type YjsConnectionState = "disconnected" | "connecting" | "syncing" | "connected";
|
|
17
|
+
/**
|
|
18
|
+
* Session participant information.
|
|
19
|
+
*/
|
|
20
|
+
interface SessionParticipant {
|
|
21
|
+
userId: string;
|
|
22
|
+
isEditing: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Session state for a collaborative document.
|
|
26
|
+
*/
|
|
27
|
+
export interface YjsSessionState {
|
|
28
|
+
active: boolean;
|
|
29
|
+
participants: SessionParticipant[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Options for useYjsDocument hook.
|
|
33
|
+
*/
|
|
34
|
+
export interface UseYjsDocumentOptions {
|
|
35
|
+
/** Initial content if document is empty */
|
|
36
|
+
initialContent?: string;
|
|
37
|
+
/** Whether to automatically connect when component mounts */
|
|
38
|
+
autoConnect?: boolean;
|
|
39
|
+
/** Whether to start in editing mode */
|
|
40
|
+
editing?: boolean;
|
|
41
|
+
/** Skip connecting (useful for conditional loading) */
|
|
42
|
+
skip?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Result of useYjsDocument hook.
|
|
46
|
+
*/
|
|
47
|
+
export interface UseYjsDocumentResult {
|
|
48
|
+
/** The Yjs document instance */
|
|
49
|
+
doc: YDoc | null;
|
|
50
|
+
/** Current connection state */
|
|
51
|
+
connectionState: YjsConnectionState;
|
|
52
|
+
/** Whether the document is connected for collaboration */
|
|
53
|
+
isConnected: boolean;
|
|
54
|
+
/** Whether there's an active collaborative session */
|
|
55
|
+
isSessionActive: boolean;
|
|
56
|
+
/** Current session participants */
|
|
57
|
+
participants: SessionParticipant[];
|
|
58
|
+
/** Current content of the document */
|
|
59
|
+
content: string;
|
|
60
|
+
/** Connect to the document */
|
|
61
|
+
connect: () => void;
|
|
62
|
+
/** Disconnect from the document */
|
|
63
|
+
disconnect: () => void;
|
|
64
|
+
/** Signal editing focus */
|
|
65
|
+
focus: () => void;
|
|
66
|
+
/** Signal editing blur */
|
|
67
|
+
blur: () => void;
|
|
68
|
+
/** Any error that occurred */
|
|
69
|
+
error: Error | null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Hook to manage a Yjs collaborative document.
|
|
73
|
+
*
|
|
74
|
+
* @param docKey - Document key identifying the field
|
|
75
|
+
* @param options - Hook options
|
|
76
|
+
* @returns UseYjsDocumentResult with document, connection state, and controls
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```tsx
|
|
80
|
+
* function TaskDescriptionEditor({ taskId }: { taskId: string }) {
|
|
81
|
+
* const {
|
|
82
|
+
* doc,
|
|
83
|
+
* isSessionActive,
|
|
84
|
+
* participants,
|
|
85
|
+
* } = useYjsDocument(
|
|
86
|
+
* { entityType: 'Task', entityId: taskId, fieldName: 'description' },
|
|
87
|
+
* { autoConnect: true, initialContent: task.description }
|
|
88
|
+
* );
|
|
89
|
+
*
|
|
90
|
+
* // doc becomes available after the first render (even offline).
|
|
91
|
+
* // Initial content is seeded into the local Y.Doc during connect(),
|
|
92
|
+
* // so the editor shows content immediately without waiting for the server.
|
|
93
|
+
* return (
|
|
94
|
+
* <div>
|
|
95
|
+
* {isSessionActive && (
|
|
96
|
+
* <div>
|
|
97
|
+
* Collaborating with: {participants.map(p => p.userId).join(', ')}
|
|
98
|
+
* </div>
|
|
99
|
+
* )}
|
|
100
|
+
* {doc && <TiptapEditor doc={doc} />}
|
|
101
|
+
* </div>
|
|
102
|
+
* );
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare const useYjsDocument: (docKey: DocumentKey, options?: UseYjsDocumentOptions) => UseYjsDocumentResult;
|
|
107
|
+
export {};
|
|
108
|
+
//# sourceMappingURL=use-yjs-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-yjs-document.d.ts","sourceRoot":"","sources":["../../src/hooks/use-yjs-document.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AAIvC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,YAAY,GACZ,SAAS,GACT,WAAW,CAAC;AAEhB;;GAEG;AACH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,kBAAkB,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,gCAAgC;IAChC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,+BAA+B;IAC/B,eAAe,EAAE,kBAAkB,CAAC;IACpC,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,OAAO,CAAC;IACzB,mCAAmC;IACnC,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mCAAmC;IACnC,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,WAAW,EACnB,UAAS,qBAA0B,KAClC,oBAuMF,CAAC"}
|