@getguru/slate-yjs-react 1.1.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +3 -0
  3. package/dist/hooks/useDecorateRemoteCursors.d.ts +24 -0
  4. package/dist/hooks/useDecorateRemoteCursors.d.ts.map +1 -0
  5. package/dist/hooks/useRemoteCursorEditor.d.ts +4 -0
  6. package/dist/hooks/useRemoteCursorEditor.d.ts.map +1 -0
  7. package/dist/hooks/useRemoteCursorOverlayPositions.d.ts +19 -0
  8. package/dist/hooks/useRemoteCursorOverlayPositions.d.ts.map +1 -0
  9. package/dist/hooks/useRemoteCursorStateStore.d.ts +5 -0
  10. package/dist/hooks/useRemoteCursorStateStore.d.ts.map +1 -0
  11. package/dist/hooks/useRemoteCursorStates.d.ts +4 -0
  12. package/dist/hooks/useRemoteCursorStates.d.ts.map +1 -0
  13. package/dist/hooks/useUnsetCursorPositionOnBlur.d.ts +2 -0
  14. package/dist/hooks/useUnsetCursorPositionOnBlur.d.ts.map +1 -0
  15. package/dist/hooks/utils.d.ts +4 -0
  16. package/dist/hooks/utils.d.ts.map +1 -0
  17. package/dist/index.cjs +453 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.global.js +31900 -0
  22. package/dist/index.global.js.map +1 -0
  23. package/dist/index.js +450 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/types.d.ts +5 -0
  26. package/dist/types.d.ts.map +1 -0
  27. package/dist/utils/getCursorRange.d.ts +4 -0
  28. package/dist/utils/getCursorRange.d.ts.map +1 -0
  29. package/dist/utils/getOverlayPosition.d.ts +24 -0
  30. package/dist/utils/getOverlayPosition.d.ts.map +1 -0
  31. package/dist/utils/react-editor-to-dom-range-safe.d.ts +4 -0
  32. package/dist/utils/react-editor-to-dom-range-safe.d.ts.map +1 -0
  33. package/package.json +55 -0
  34. package/src/hooks/useDecorateRemoteCursors.ts +125 -0
  35. package/src/hooks/useRemoteCursorEditor.ts +15 -0
  36. package/src/hooks/useRemoteCursorOverlayPositions.tsx +144 -0
  37. package/src/hooks/useRemoteCursorStateStore.ts +89 -0
  38. package/src/hooks/useRemoteCursorStates.ts +28 -0
  39. package/src/hooks/useUnsetCursorPositionOnBlur.ts +48 -0
  40. package/src/hooks/utils.ts +61 -0
  41. package/src/index.ts +26 -0
  42. package/src/types.ts +4 -0
  43. package/src/utils/getCursorRange.ts +45 -0
  44. package/src/utils/getOverlayPosition.ts +111 -0
  45. package/src/utils/react-editor-to-dom-range-safe.ts +13 -0
  46. package/tsconfig.json +11 -0
  47. package/tsup.config.ts +32 -0
package/dist/index.js ADDED
@@ -0,0 +1,450 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+
33
+ // src/hooks/useDecorateRemoteCursors.ts
34
+ import { useCallback, useRef } from "react";
35
+ import { Range } from "slate";
36
+
37
+ // src/utils/getCursorRange.ts
38
+ import {
39
+ relativeRangeToSlateRange
40
+ } from "@getguru/slate-yjs-core";
41
+ var CHILDREN_TO_CURSOR_STATE_TO_RANGE = /* @__PURE__ */ new WeakMap();
42
+ function getCursorRange(editor, cursorState) {
43
+ if (!cursorState.relativeSelection) {
44
+ return null;
45
+ }
46
+ let cursorStates = CHILDREN_TO_CURSOR_STATE_TO_RANGE.get(editor.children);
47
+ if (!cursorStates) {
48
+ cursorStates = /* @__PURE__ */ new WeakMap();
49
+ CHILDREN_TO_CURSOR_STATE_TO_RANGE.set(editor.children, cursorStates);
50
+ }
51
+ let range = cursorStates.get(cursorState);
52
+ if (range === void 0) {
53
+ try {
54
+ range = relativeRangeToSlateRange(editor.sharedRoot, editor, cursorState.relativeSelection);
55
+ cursorStates.set(cursorState, range);
56
+ } catch (e) {
57
+ return null;
58
+ }
59
+ }
60
+ return range;
61
+ }
62
+
63
+ // src/hooks/useRemoteCursorEditor.ts
64
+ import { CursorEditor as CursorEditor2 } from "@getguru/slate-yjs-core";
65
+ import { useSlateStatic } from "slate-react";
66
+ function useRemoteCursorEditor() {
67
+ const editor = useSlateStatic();
68
+ if (!CursorEditor2.isCursorEditor(editor)) {
69
+ throw new Error("Cannot use useSyncExternalStore outside the context of a RemoteCursorEditor");
70
+ }
71
+ return editor;
72
+ }
73
+
74
+ // src/hooks/useRemoteCursorStates.ts
75
+ import { useSyncExternalStore } from "use-sync-external-store/shim";
76
+ import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector";
77
+
78
+ // src/hooks/useRemoteCursorStateStore.ts
79
+ import {
80
+ CursorEditor as CursorEditor3
81
+ } from "@getguru/slate-yjs-core";
82
+ var EDITOR_TO_CURSOR_STORE = /* @__PURE__ */ new WeakMap();
83
+ function createRemoteCursorStateStore(editor) {
84
+ let cursors = {};
85
+ const changed = /* @__PURE__ */ new Set();
86
+ const addChanged = changed.add.bind(changed);
87
+ const onStoreChangeListeners = /* @__PURE__ */ new Set();
88
+ let changeHandler = null;
89
+ const subscribe = (onStoreChange) => {
90
+ onStoreChangeListeners.add(onStoreChange);
91
+ if (!changeHandler) {
92
+ changeHandler = (event) => {
93
+ event.added.forEach(addChanged);
94
+ event.removed.forEach(addChanged);
95
+ event.updated.forEach(addChanged);
96
+ onStoreChangeListeners.forEach((listener) => listener());
97
+ };
98
+ CursorEditor3.on(editor, "change", changeHandler);
99
+ }
100
+ return () => {
101
+ onStoreChangeListeners.delete(onStoreChange);
102
+ if (changeHandler && onStoreChangeListeners.size === 0) {
103
+ CursorEditor3.off(editor, "change", changeHandler);
104
+ changeHandler = null;
105
+ }
106
+ };
107
+ };
108
+ const getSnapshot = () => {
109
+ if (changed.size === 0) {
110
+ return cursors;
111
+ }
112
+ changed.forEach((clientId) => {
113
+ const state = CursorEditor3.cursorState(editor, clientId);
114
+ if (state === null) {
115
+ delete cursors[clientId.toString()];
116
+ return;
117
+ }
118
+ cursors[clientId] = state;
119
+ });
120
+ changed.clear();
121
+ cursors = __spreadValues({}, cursors);
122
+ return cursors;
123
+ };
124
+ return [subscribe, getSnapshot];
125
+ }
126
+ function getCursorStateStore(editor) {
127
+ const existing = EDITOR_TO_CURSOR_STORE.get(editor);
128
+ if (existing) {
129
+ return existing;
130
+ }
131
+ const store = createRemoteCursorStateStore(editor);
132
+ EDITOR_TO_CURSOR_STORE.set(editor, store);
133
+ return store;
134
+ }
135
+ function useRemoteCursorStateStore() {
136
+ const editor = useRemoteCursorEditor();
137
+ return getCursorStateStore(editor);
138
+ }
139
+
140
+ // src/hooks/useRemoteCursorStates.ts
141
+ function useRemoteCursorStates() {
142
+ const [subscribe, getSnapshot] = useRemoteCursorStateStore();
143
+ return useSyncExternalStore(subscribe, getSnapshot);
144
+ }
145
+ function useRemoteCursorStatesSelector(selector, isEqual) {
146
+ const [subscribe, getSnapshot] = useRemoteCursorStateStore();
147
+ return useSyncExternalStoreWithSelector(subscribe, getSnapshot, null, selector, isEqual);
148
+ }
149
+
150
+ // src/hooks/useDecorateRemoteCursors.ts
151
+ var REMOTE_CURSOR_DECORATION_PREFIX = "remote-cursor-";
152
+ var REMOTE_CURSOR_CARET_DECORATION_PREFIX = "remote-caret-";
153
+ function getRemoteCursorsOnLeaf(leaf) {
154
+ return Object.entries(leaf).filter(([key]) => key.startsWith(REMOTE_CURSOR_DECORATION_PREFIX)).map(([, data]) => data);
155
+ }
156
+ function getRemoteCaretsOnLeaf(leaf) {
157
+ return Object.entries(leaf).filter(([key]) => key.startsWith(REMOTE_CURSOR_CARET_DECORATION_PREFIX)).map(([, data]) => data);
158
+ }
159
+ function getDecoration(clientId, state, range, caret) {
160
+ if (!caret) {
161
+ const key2 = `${REMOTE_CURSOR_DECORATION_PREFIX}${clientId}`;
162
+ return __spreadProps(__spreadValues({}, range), { [key2]: state });
163
+ }
164
+ const key = `${REMOTE_CURSOR_CARET_DECORATION_PREFIX}${clientId}`;
165
+ return __spreadProps(__spreadValues({}, range), {
166
+ anchor: range.focus,
167
+ [key]: state
168
+ });
169
+ }
170
+ function useDecorateRemoteCursors({ carets = true } = {}) {
171
+ const editor = useRemoteCursorEditor();
172
+ const cursors = useRemoteCursorStates();
173
+ const cursorsRef = useRef(cursors);
174
+ cursorsRef.current = cursors;
175
+ return useCallback((entry) => {
176
+ const [, path] = entry;
177
+ if (path.length !== 0) {
178
+ return [];
179
+ }
180
+ return Object.entries(cursorsRef.current).flatMap(([clientId, state]) => {
181
+ const range = getCursorRange(editor, state);
182
+ if (!range) {
183
+ return [];
184
+ }
185
+ if (carets && Range.isCollapsed(range)) {
186
+ return getDecoration(clientId, state, range, true);
187
+ }
188
+ if (!carets) {
189
+ return getDecoration(clientId, state, range, false);
190
+ }
191
+ return [
192
+ getDecoration(clientId, state, range, false),
193
+ getDecoration(clientId, state, range, true)
194
+ ];
195
+ });
196
+ }, [carets, editor]);
197
+ }
198
+
199
+ // src/hooks/useUnsetCursorPositionOnBlur.ts
200
+ import { CursorEditor as CursorEditor4 } from "@getguru/slate-yjs-core";
201
+ import { useCallback as useCallback2, useEffect } from "react";
202
+ import { useFocused } from "slate-react";
203
+ function useUnsetCursorPositionOnBlur() {
204
+ const editor = useRemoteCursorEditor();
205
+ const isSlateFocused = useFocused();
206
+ const sendCursorPosition = useCallback2((isFocused) => {
207
+ if (isFocused && editor.selection) {
208
+ CursorEditor4.sendCursorPosition(editor, editor.selection);
209
+ return;
210
+ }
211
+ if (!isFocused) {
212
+ CursorEditor4.sendCursorPosition(editor, null);
213
+ }
214
+ }, [editor]);
215
+ useEffect(() => {
216
+ const handleWindowBlur = () => {
217
+ if (isSlateFocused) {
218
+ sendCursorPosition(false);
219
+ }
220
+ };
221
+ const handleWindowFocus = () => {
222
+ if (isSlateFocused) {
223
+ sendCursorPosition(true);
224
+ }
225
+ };
226
+ window.addEventListener("blur", handleWindowBlur);
227
+ window.addEventListener("focus", handleWindowFocus);
228
+ return () => {
229
+ window.removeEventListener("blur", handleWindowBlur);
230
+ window.removeEventListener("focus", handleWindowFocus);
231
+ };
232
+ }, [isSlateFocused, sendCursorPosition]);
233
+ useEffect(() => {
234
+ sendCursorPosition(isSlateFocused);
235
+ }, [editor, isSlateFocused, sendCursorPosition]);
236
+ }
237
+
238
+ // src/hooks/useRemoteCursorOverlayPositions.tsx
239
+ import {
240
+ useCallback as useCallback4,
241
+ useLayoutEffect,
242
+ useMemo,
243
+ useRef as useRef3,
244
+ useState as useState2
245
+ } from "react";
246
+
247
+ // src/utils/getOverlayPosition.ts
248
+ import { Editor, Path, Range as Range2, Text } from "slate";
249
+ import { ReactEditor as ReactEditor3 } from "slate-react";
250
+
251
+ // src/utils/react-editor-to-dom-range-safe.ts
252
+ import { ReactEditor as ReactEditor2 } from "slate-react";
253
+ function reactEditorToDomRangeSafe(editor, range) {
254
+ try {
255
+ return ReactEditor2.toDOMRange(editor, range);
256
+ } catch (e) {
257
+ return null;
258
+ }
259
+ }
260
+
261
+ // src/utils/getOverlayPosition.ts
262
+ function getOverlayPosition(editor, range, { yOffset, xOffset, shouldGenerateOverlay }) {
263
+ const [start, end] = Range2.edges(range);
264
+ const domRange = reactEditorToDomRangeSafe(editor, range);
265
+ if (!domRange) {
266
+ return {
267
+ caretPosition: null,
268
+ selectionRects: []
269
+ };
270
+ }
271
+ const selectionRects = [];
272
+ const nodeIterator = Editor.nodes(editor, {
273
+ at: range,
274
+ match: (n, p) => Text.isText(n) && (!shouldGenerateOverlay || shouldGenerateOverlay(n, p))
275
+ });
276
+ let caretPosition = null;
277
+ const isBackward = Range2.isBackward(range);
278
+ for (const [node, path] of nodeIterator) {
279
+ const domNode = ReactEditor3.toDOMNode(editor, node);
280
+ const isStartNode = Path.equals(path, start.path);
281
+ const isEndNode = Path.equals(path, end.path);
282
+ let clientRects = null;
283
+ if (isStartNode || isEndNode) {
284
+ const nodeRange = document.createRange();
285
+ nodeRange.selectNode(domNode);
286
+ if (isStartNode) {
287
+ nodeRange.setStart(domRange.startContainer, domRange.startOffset);
288
+ }
289
+ if (isEndNode) {
290
+ nodeRange.setEnd(domRange.endContainer, domRange.endOffset);
291
+ }
292
+ clientRects = nodeRange.getClientRects();
293
+ } else {
294
+ clientRects = domNode.getClientRects();
295
+ }
296
+ const isCaret = isBackward ? isStartNode : isEndNode;
297
+ for (let i = 0; i < clientRects.length; i++) {
298
+ const clientRect = clientRects.item(i);
299
+ if (!clientRect) {
300
+ continue;
301
+ }
302
+ const isCaretRect = isCaret && (isBackward ? i === 0 : i === clientRects.length - 1);
303
+ const top = clientRect.top - yOffset;
304
+ const left = clientRect.left - xOffset;
305
+ if (isCaretRect) {
306
+ caretPosition = {
307
+ height: clientRect.height,
308
+ top,
309
+ left: left + (isBackward || Range2.isCollapsed(range) ? 0 : clientRect.width)
310
+ };
311
+ }
312
+ selectionRects.push({
313
+ width: clientRect.width,
314
+ height: clientRect.height,
315
+ top,
316
+ left
317
+ });
318
+ }
319
+ }
320
+ return {
321
+ selectionRects,
322
+ caretPosition
323
+ };
324
+ }
325
+
326
+ // src/hooks/utils.ts
327
+ import {
328
+ useCallback as useCallback3,
329
+ useEffect as useEffect2,
330
+ useReducer,
331
+ useRef as useRef2,
332
+ useState
333
+ } from "react";
334
+ function useRequestRerender() {
335
+ const [, rerender] = useReducer((s) => s + 1, 0);
336
+ const animationFrameIdRef = useRef2(null);
337
+ const clearAnimationFrame = () => {
338
+ if (animationFrameIdRef.current) {
339
+ cancelAnimationFrame(animationFrameIdRef.current);
340
+ animationFrameIdRef.current = 0;
341
+ }
342
+ };
343
+ useEffect2(clearAnimationFrame);
344
+ useEffect2(() => clearAnimationFrame, []);
345
+ return useCallback3((immediately = false) => {
346
+ if (immediately) {
347
+ rerender();
348
+ return;
349
+ }
350
+ if (animationFrameIdRef.current) {
351
+ return;
352
+ }
353
+ animationFrameIdRef.current = requestAnimationFrame(rerender);
354
+ }, []);
355
+ }
356
+ function useOnResize(ref, onResize) {
357
+ const onResizeRef = useRef2(onResize);
358
+ onResizeRef.current = onResize;
359
+ const [observer] = useState(() => new ResizeObserver(() => {
360
+ onResizeRef.current();
361
+ }));
362
+ useEffect2(() => {
363
+ if (!(ref == null ? void 0 : ref.current)) {
364
+ return;
365
+ }
366
+ const { current: element } = ref;
367
+ observer.observe(element);
368
+ return () => observer.unobserve(element);
369
+ }, [observer, ref]);
370
+ }
371
+
372
+ // src/hooks/useRemoteCursorOverlayPositions.tsx
373
+ var FROZEN_EMPTY_ARRAY = Object.freeze([]);
374
+ function useRemoteCursorOverlayPositions(_a = {}) {
375
+ var _b = _a, {
376
+ containerRef,
377
+ shouldGenerateOverlay
378
+ } = _b, opts = __objRest(_b, [
379
+ "containerRef",
380
+ "shouldGenerateOverlay"
381
+ ]);
382
+ var _a2;
383
+ const editor = useRemoteCursorEditor();
384
+ const cursorStates = useRemoteCursorStates();
385
+ const requestRerender = useRequestRerender();
386
+ const overlayPositionCache = useRef3(/* @__PURE__ */ new WeakMap());
387
+ const [overlayPositions, setOverlayPositions] = useState2({});
388
+ const refreshOnResize = "refreshOnResize" in opts ? (_a2 = opts.refreshOnResize) != null ? _a2 : true : true;
389
+ useOnResize(refreshOnResize ? containerRef : void 0, () => {
390
+ overlayPositionCache.current = /* @__PURE__ */ new WeakMap();
391
+ requestRerender(refreshOnResize !== "debounced");
392
+ });
393
+ useLayoutEffect(() => {
394
+ var _a3, _b2, _c;
395
+ if (containerRef && !containerRef.current) {
396
+ return;
397
+ }
398
+ const containerRect = (_a3 = containerRef == null ? void 0 : containerRef.current) == null ? void 0 : _a3.getBoundingClientRect();
399
+ const xOffset = (_b2 = containerRect == null ? void 0 : containerRect.x) != null ? _b2 : 0;
400
+ const yOffset = (_c = containerRect == null ? void 0 : containerRect.y) != null ? _c : 0;
401
+ let overlayPositionsChanged = Object.keys(overlayPositions).length !== Object.keys(cursorStates).length;
402
+ const updated = Object.fromEntries(Object.entries(cursorStates).map(([key, state]) => {
403
+ const range = state.relativeSelection && getCursorRange(editor, state);
404
+ if (!range) {
405
+ return [key, FROZEN_EMPTY_ARRAY];
406
+ }
407
+ const cached = overlayPositionCache.current.get(range);
408
+ if (cached) {
409
+ return [key, cached];
410
+ }
411
+ const overlayPosition = getOverlayPosition(editor, range, {
412
+ xOffset,
413
+ yOffset,
414
+ shouldGenerateOverlay
415
+ });
416
+ overlayPositionsChanged = true;
417
+ overlayPositionCache.current.set(range, overlayPosition);
418
+ return [key, overlayPosition];
419
+ }));
420
+ if (overlayPositionsChanged) {
421
+ setOverlayPositions(updated);
422
+ }
423
+ });
424
+ const overlayData = useMemo(() => Object.entries(cursorStates).map(([clientId, state]) => {
425
+ var _a3, _b2;
426
+ const range = state.relativeSelection && getCursorRange(editor, state);
427
+ const overlayPosition = overlayPositions[clientId];
428
+ return __spreadProps(__spreadValues({}, state), {
429
+ range,
430
+ caretPosition: (_a3 = overlayPosition == null ? void 0 : overlayPosition.caretPosition) != null ? _a3 : null,
431
+ selectionRects: (_b2 = overlayPosition == null ? void 0 : overlayPosition.selectionRects) != null ? _b2 : FROZEN_EMPTY_ARRAY
432
+ });
433
+ }), [cursorStates, editor, overlayPositions]);
434
+ const refresh = useCallback4(() => {
435
+ overlayPositionCache.current = /* @__PURE__ */ new WeakMap();
436
+ requestRerender(true);
437
+ }, [requestRerender]);
438
+ return [overlayData, refresh];
439
+ }
440
+ export {
441
+ getCursorRange,
442
+ getRemoteCaretsOnLeaf,
443
+ getRemoteCursorsOnLeaf,
444
+ useDecorateRemoteCursors,
445
+ useRemoteCursorOverlayPositions,
446
+ useRemoteCursorStates,
447
+ useRemoteCursorStatesSelector,
448
+ useUnsetCursorPositionOnBlur
449
+ };
450
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useDecorateRemoteCursors.ts","../src/utils/getCursorRange.ts","../src/hooks/useRemoteCursorEditor.ts","../src/hooks/useRemoteCursorStates.ts","../src/hooks/useRemoteCursorStateStore.ts","../src/hooks/useUnsetCursorPositionOnBlur.ts","../src/hooks/useRemoteCursorOverlayPositions.tsx","../src/utils/getOverlayPosition.ts","../src/utils/react-editor-to-dom-range-safe.ts","../src/hooks/utils.ts"],"sourcesContent":["import { CursorState } from '@getguru/slate-yjs-core';\nimport { useCallback, useRef } from 'react';\nimport { BaseRange, BaseText, NodeEntry, Range } from 'slate';\nimport { getCursorRange } from '../utils/getCursorRange';\nimport { useRemoteCursorEditor } from './useRemoteCursorEditor';\nimport { useRemoteCursorStates } from './useRemoteCursorStates';\n\nexport const REMOTE_CURSOR_DECORATION_PREFIX = 'remote-cursor-';\nexport const REMOTE_CURSOR_CARET_DECORATION_PREFIX = 'remote-caret-';\n\nexport type RemoteCaretDecoration<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = {\n [\n key: `${typeof REMOTE_CURSOR_CARET_DECORATION_PREFIX}${string}`\n ]: CursorState<TCursorData> & { isBackward: boolean };\n};\n\nexport type RemoteCursorDecoration<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = {\n [\n key: `${typeof REMOTE_CURSOR_DECORATION_PREFIX}${string}`\n ]: CursorState<TCursorData>;\n};\n\nexport type RemoteCursorDecoratedRange<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = BaseRange & RemoteCursorDecoration<TCursorData>;\n\nexport type RemoteCaretDecoratedRange<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = BaseRange & RemoteCaretDecoration<TCursorData>;\n\nexport type TextWithRemoteCursors<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = BaseText &\n RemoteCursorDecoration<TCursorData> &\n RemoteCaretDecoration<TCursorData>;\n\nexport function getRemoteCursorsOnLeaf<\n TCursorData extends Record<string, unknown>,\n TLeaf extends TextWithRemoteCursors<TCursorData>\n>(leaf: TLeaf): CursorState<TCursorData>[] {\n return Object.entries(leaf)\n .filter(([key]) => key.startsWith(REMOTE_CURSOR_DECORATION_PREFIX))\n .map(([, data]) => data);\n}\n\nexport function getRemoteCaretsOnLeaf<\n TCursorData extends Record<string, unknown>,\n TLeaf extends TextWithRemoteCursors<TCursorData>\n>(leaf: TLeaf): (CursorState<TCursorData> & { isBackward: boolean })[] {\n return Object.entries(leaf)\n .filter(([key]) => key.startsWith(REMOTE_CURSOR_CARET_DECORATION_PREFIX))\n .map(([, data]) => data);\n}\n\nexport type UseDecorateRemoteCursorsOptions = {\n carets?: boolean;\n};\n\nfunction getDecoration<\n TCursorData extends Record<string, unknown>,\n TCaret extends boolean\n>(\n clientId: string,\n state: CursorState<TCursorData>,\n range: BaseRange,\n caret: TCaret\n): TCaret extends true\n ? RemoteCursorDecoratedRange<TCursorData>\n : RemoteCaretDecoratedRange<TCursorData> {\n if (!caret) {\n const key = `${REMOTE_CURSOR_DECORATION_PREFIX}${clientId}`;\n return { ...range, [key]: state };\n }\n\n const key = `${REMOTE_CURSOR_CARET_DECORATION_PREFIX}${clientId}`;\n return {\n ...range,\n anchor: range.focus,\n [key]: state,\n };\n}\n\nexport function useDecorateRemoteCursors<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n>({ carets = true }: UseDecorateRemoteCursorsOptions = {}) {\n const editor = useRemoteCursorEditor<TCursorData>();\n const cursors = useRemoteCursorStates<TCursorData>();\n\n const cursorsRef = useRef(cursors);\n cursorsRef.current = cursors;\n\n return useCallback(\n (entry: NodeEntry) => {\n const [, path] = entry;\n if (path.length !== 0) {\n return [];\n }\n\n return Object.entries(cursorsRef.current).flatMap(([clientId, state]) => {\n const range = getCursorRange(editor, state);\n if (!range) {\n return [];\n }\n\n if (carets && Range.isCollapsed(range)) {\n return getDecoration(clientId, state, range, true);\n }\n\n if (!carets) {\n return getDecoration(clientId, state, range, false);\n }\n\n return [\n getDecoration(clientId, state, range, false),\n getDecoration(clientId, state, range, true),\n ];\n });\n },\n [carets, editor]\n );\n}\n","import {\n CursorEditor,\n CursorState,\n relativeRangeToSlateRange,\n} from '@getguru/slate-yjs-core';\nimport { BaseRange, Descendant, Range } from 'slate';\n\nconst CHILDREN_TO_CURSOR_STATE_TO_RANGE: WeakMap<\n Descendant[],\n WeakMap<CursorState, Range | null>\n> = new WeakMap();\n\nexport function getCursorRange<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n>(\n editor: CursorEditor<TCursorData>,\n cursorState: CursorState<TCursorData>\n): BaseRange | null {\n if (!cursorState.relativeSelection) {\n return null;\n }\n\n let cursorStates = CHILDREN_TO_CURSOR_STATE_TO_RANGE.get(editor.children);\n if (!cursorStates) {\n cursorStates = new WeakMap();\n CHILDREN_TO_CURSOR_STATE_TO_RANGE.set(editor.children, cursorStates);\n }\n\n let range = cursorStates.get(cursorState);\n if (range === undefined) {\n try {\n range = relativeRangeToSlateRange(\n editor.sharedRoot,\n editor,\n cursorState.relativeSelection\n );\n\n cursorStates.set(cursorState, range);\n } catch (e) {\n return null;\n }\n }\n\n return range;\n}\n","import { CursorEditor } from '@getguru/slate-yjs-core';\nimport { ReactEditor, useSlateStatic } from 'slate-react';\n\nexport function useRemoteCursorEditor<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n>(): CursorEditor<TCursorData> & ReactEditor {\n const editor = useSlateStatic();\n if (!CursorEditor.isCursorEditor(editor)) {\n throw new Error(\n 'Cannot use useSyncExternalStore outside the context of a RemoteCursorEditor'\n );\n }\n\n return editor as CursorEditor & ReactEditor;\n}\n","import { CursorState } from '@getguru/slate-yjs-core';\nimport { useSyncExternalStore } from 'use-sync-external-store/shim';\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';\nimport { useRemoteCursorStateStore } from './useRemoteCursorStateStore';\n\nexport function useRemoteCursorStates<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n>() {\n const [subscribe, getSnapshot] = useRemoteCursorStateStore<TCursorData>();\n return useSyncExternalStore(subscribe, getSnapshot);\n}\n\nexport function useRemoteCursorStatesSelector<\n TCursorData extends Record<string, unknown> = Record<string, unknown>,\n TSelection = unknown\n>(\n selector: (cursors: Record<string, CursorState<TCursorData>>) => TSelection,\n isEqual?: (a: TSelection, b: TSelection) => boolean\n): TSelection {\n const [subscribe, getSnapshot] = useRemoteCursorStateStore<TCursorData>();\n return useSyncExternalStoreWithSelector(\n subscribe,\n getSnapshot,\n null,\n selector,\n isEqual\n );\n}\n","import {\n CursorEditor,\n CursorState,\n RemoteCursorChangeEventListener,\n} from '@getguru/slate-yjs-core';\nimport { BaseEditor } from 'slate';\nimport { Store } from '../types';\nimport { useRemoteCursorEditor } from './useRemoteCursorEditor';\n\nexport type CursorStore<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n> = Store<Record<string, CursorState<TCursorData>>>;\n\nconst EDITOR_TO_CURSOR_STORE: WeakMap<BaseEditor, CursorStore> = new WeakMap();\n\nfunction createRemoteCursorStateStore<\n TCursorData extends Record<string, unknown>\n>(editor: CursorEditor<TCursorData>): CursorStore<TCursorData> {\n let cursors: Record<string, CursorState<TCursorData>> = {};\n\n const changed = new Set<number>();\n const addChanged = changed.add.bind(changed);\n const onStoreChangeListeners: Set<() => void> = new Set();\n\n let changeHandler: RemoteCursorChangeEventListener | null = null;\n\n const subscribe = (onStoreChange: () => void) => {\n onStoreChangeListeners.add(onStoreChange);\n if (!changeHandler) {\n changeHandler = (event) => {\n event.added.forEach(addChanged);\n event.removed.forEach(addChanged);\n event.updated.forEach(addChanged);\n onStoreChangeListeners.forEach((listener) => listener());\n };\n CursorEditor.on(editor, 'change', changeHandler);\n }\n\n return () => {\n onStoreChangeListeners.delete(onStoreChange);\n if (changeHandler && onStoreChangeListeners.size === 0) {\n CursorEditor.off(editor, 'change', changeHandler);\n changeHandler = null;\n }\n };\n };\n\n const getSnapshot = () => {\n if (changed.size === 0) {\n return cursors;\n }\n\n changed.forEach((clientId) => {\n const state = CursorEditor.cursorState(editor, clientId);\n if (state === null) {\n delete cursors[clientId.toString()];\n return;\n }\n\n cursors[clientId] = state;\n });\n\n changed.clear();\n cursors = { ...cursors };\n return cursors;\n };\n\n return [subscribe, getSnapshot];\n}\n\nfunction getCursorStateStore<TCursorData extends Record<string, unknown>>(\n editor: CursorEditor<TCursorData>\n): CursorStore<TCursorData> {\n const existing = EDITOR_TO_CURSOR_STORE.get(editor);\n if (existing) {\n return existing as CursorStore<TCursorData>;\n }\n\n const store = createRemoteCursorStateStore(editor);\n EDITOR_TO_CURSOR_STORE.set(editor, store);\n return store;\n}\n\nexport function useRemoteCursorStateStore<\n TCursorData extends Record<string, unknown> = Record<string, unknown>\n>() {\n const editor = useRemoteCursorEditor<TCursorData>();\n return getCursorStateStore(editor);\n}\n","import { CursorEditor } from '@getguru/slate-yjs-core';\nimport { useCallback, useEffect } from 'react';\nimport { useFocused } from 'slate-react';\nimport { useRemoteCursorEditor } from './useRemoteCursorEditor';\n\nexport function useUnsetCursorPositionOnBlur() {\n const editor = useRemoteCursorEditor();\n const isSlateFocused = useFocused();\n\n const sendCursorPosition = useCallback(\n (isFocused?: boolean) => {\n if (isFocused && editor.selection) {\n CursorEditor.sendCursorPosition(editor, editor.selection);\n return;\n }\n\n if (!isFocused) {\n CursorEditor.sendCursorPosition(editor, null);\n }\n },\n [editor]\n );\n\n useEffect(() => {\n const handleWindowBlur = () => {\n if (isSlateFocused) {\n sendCursorPosition(false);\n }\n };\n\n const handleWindowFocus = () => {\n if (isSlateFocused) {\n sendCursorPosition(true);\n }\n };\n\n window.addEventListener('blur', handleWindowBlur);\n window.addEventListener('focus', handleWindowFocus);\n return () => {\n window.removeEventListener('blur', handleWindowBlur);\n window.removeEventListener('focus', handleWindowFocus);\n };\n }, [isSlateFocused, sendCursorPosition]);\n\n useEffect(() => {\n sendCursorPosition(isSlateFocused);\n }, [editor, isSlateFocused, sendCursorPosition]);\n}\n","import { CursorState } from '@getguru/slate-yjs-core';\nimport {\n RefObject,\n useCallback,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { BaseRange, NodeMatch, Text } from 'slate';\nimport { getCursorRange } from '../utils/getCursorRange';\nimport {\n CaretPosition,\n getOverlayPosition,\n OverlayPosition,\n SelectionRect,\n} from '../utils/getOverlayPosition';\nimport { useRemoteCursorEditor } from './useRemoteCursorEditor';\nimport { useRemoteCursorStates } from './useRemoteCursorStates';\nimport { useOnResize, useRequestRerender } from './utils';\n\nconst FROZEN_EMPTY_ARRAY = Object.freeze([]);\n\nexport type UseRemoteCursorOverlayPositionsOptions<T extends HTMLElement> = {\n shouldGenerateOverlay?: NodeMatch<Text>;\n} & (\n | {\n // Container the overlay will be rendered in. If set, all returned overlay positions\n // will be relative to this container and the cursor positions will be automatically\n // updated on container resize.\n containerRef?: undefined;\n }\n | {\n containerRef: RefObject<T>;\n\n // Whether to refresh the cursor overlay positions on container resize. Defaults\n // to true. If set to 'debounced', the remote cursor positions will be updated\n // each animation frame.\n refreshOnResize?: boolean | 'debounced';\n }\n);\n\nexport type CursorOverlayData<TCursorData extends Record<string, unknown>> =\n CursorState<TCursorData> & {\n range: BaseRange | null;\n caretPosition: CaretPosition | null;\n selectionRects: SelectionRect[];\n };\n\nexport function useRemoteCursorOverlayPositions<\n TCursorData extends Record<string, unknown>,\n TContainer extends HTMLElement = HTMLDivElement\n>({\n containerRef,\n shouldGenerateOverlay,\n ...opts\n}: UseRemoteCursorOverlayPositionsOptions<TContainer> = {}) {\n const editor = useRemoteCursorEditor<TCursorData>();\n const cursorStates = useRemoteCursorStates<TCursorData>();\n const requestRerender = useRequestRerender();\n\n const overlayPositionCache = useRef(\n new WeakMap<BaseRange, OverlayPosition>()\n );\n const [overlayPositions, setOverlayPositions] = useState<\n Record<string, OverlayPosition>\n >({});\n\n const refreshOnResize =\n 'refreshOnResize' in opts ? opts.refreshOnResize ?? true : true;\n\n useOnResize(refreshOnResize ? containerRef : undefined, () => {\n overlayPositionCache.current = new WeakMap();\n requestRerender(refreshOnResize !== 'debounced');\n });\n\n // Update selection rects after paint\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\n // We have a container ref but the ref is null => container\n // isn't mounted to we can't calculate the selection rects.\n if (containerRef && !containerRef.current) {\n return;\n }\n\n const containerRect = containerRef?.current?.getBoundingClientRect();\n const xOffset = containerRect?.x ?? 0;\n const yOffset = containerRect?.y ?? 0;\n\n let overlayPositionsChanged =\n Object.keys(overlayPositions).length !== Object.keys(cursorStates).length;\n\n const updated = Object.fromEntries(\n Object.entries(cursorStates).map(([key, state]) => {\n const range = state.relativeSelection && getCursorRange(editor, state);\n\n if (!range) {\n return [key, FROZEN_EMPTY_ARRAY];\n }\n\n const cached = overlayPositionCache.current.get(range);\n if (cached) {\n return [key, cached];\n }\n\n const overlayPosition = getOverlayPosition(editor, range, {\n xOffset,\n yOffset,\n shouldGenerateOverlay,\n });\n overlayPositionsChanged = true;\n overlayPositionCache.current.set(range, overlayPosition);\n return [key, overlayPosition];\n })\n );\n\n if (overlayPositionsChanged) {\n setOverlayPositions(updated);\n }\n });\n\n const overlayData = useMemo<CursorOverlayData<TCursorData>[]>(\n () =>\n Object.entries(cursorStates).map(([clientId, state]) => {\n const range = state.relativeSelection && getCursorRange(editor, state);\n const overlayPosition = overlayPositions[clientId];\n\n return {\n ...state,\n range,\n caretPosition: overlayPosition?.caretPosition ?? null,\n selectionRects: overlayPosition?.selectionRects ?? FROZEN_EMPTY_ARRAY,\n };\n }),\n [cursorStates, editor, overlayPositions]\n );\n\n const refresh = useCallback(() => {\n overlayPositionCache.current = new WeakMap();\n requestRerender(true);\n }, [requestRerender]);\n\n return [overlayData, refresh] as const;\n}\n","import { BaseRange, Editor, Path, Range, Text } from 'slate';\nimport { ReactEditor } from 'slate-react';\nimport { reactEditorToDomRangeSafe } from './react-editor-to-dom-range-safe';\n\nexport type SelectionRect = {\n width: number;\n height: number;\n top: number;\n left: number;\n};\n\nexport type CaretPosition = {\n height: number;\n top: number;\n left: number;\n};\n\nexport type OverlayPosition = {\n caretPosition: CaretPosition | null;\n selectionRects: SelectionRect[];\n};\n\nexport type GetSelectionRectsOptions = {\n xOffset: number;\n yOffset: number;\n shouldGenerateOverlay?: (node: Text, path: Path) => boolean;\n};\n\nexport function getOverlayPosition(\n editor: ReactEditor,\n range: BaseRange,\n { yOffset, xOffset, shouldGenerateOverlay }: GetSelectionRectsOptions\n): OverlayPosition {\n const [start, end] = Range.edges(range);\n const domRange = reactEditorToDomRangeSafe(editor, range);\n if (!domRange) {\n return {\n caretPosition: null,\n selectionRects: [],\n };\n }\n\n const selectionRects: SelectionRect[] = [];\n const nodeIterator = Editor.nodes(editor, {\n at: range,\n match: (n, p) =>\n Text.isText(n) && (!shouldGenerateOverlay || shouldGenerateOverlay(n, p)),\n });\n\n let caretPosition: CaretPosition | null = null;\n const isBackward = Range.isBackward(range);\n for (const [node, path] of nodeIterator) {\n const domNode = ReactEditor.toDOMNode(editor, node);\n\n const isStartNode = Path.equals(path, start.path);\n const isEndNode = Path.equals(path, end.path);\n\n let clientRects: DOMRectList | null = null;\n if (isStartNode || isEndNode) {\n const nodeRange = document.createRange();\n nodeRange.selectNode(domNode);\n\n if (isStartNode) {\n nodeRange.setStart(domRange.startContainer, domRange.startOffset);\n }\n if (isEndNode) {\n nodeRange.setEnd(domRange.endContainer, domRange.endOffset);\n }\n\n clientRects = nodeRange.getClientRects();\n } else {\n clientRects = domNode.getClientRects();\n }\n\n const isCaret = isBackward ? isStartNode : isEndNode;\n for (let i = 0; i < clientRects.length; i++) {\n const clientRect = clientRects.item(i);\n if (!clientRect) {\n continue;\n }\n\n const isCaretRect =\n isCaret && (isBackward ? i === 0 : i === clientRects.length - 1);\n\n const top = clientRect.top - yOffset;\n const left = clientRect.left - xOffset;\n\n if (isCaretRect) {\n caretPosition = {\n height: clientRect.height,\n top,\n left:\n left +\n (isBackward || Range.isCollapsed(range) ? 0 : clientRect.width),\n };\n }\n\n selectionRects.push({\n width: clientRect.width,\n height: clientRect.height,\n top,\n left,\n });\n }\n }\n\n return {\n selectionRects,\n caretPosition,\n };\n}\n","import { ReactEditor } from 'slate-react';\nimport { BaseRange } from 'slate';\n\nexport function reactEditorToDomRangeSafe(\n editor: ReactEditor,\n range: BaseRange\n): Range | null {\n try {\n return ReactEditor.toDOMRange(editor, range);\n } catch (e) {\n return null;\n }\n}\n","import {\n RefObject,\n useCallback,\n useEffect,\n useReducer,\n useRef,\n useState,\n} from 'react';\n\nexport function useRequestRerender() {\n const [, rerender] = useReducer((s) => s + 1, 0);\n const animationFrameIdRef = useRef<number | null>(null);\n\n const clearAnimationFrame = () => {\n if (animationFrameIdRef.current) {\n cancelAnimationFrame(animationFrameIdRef.current);\n animationFrameIdRef.current = 0;\n }\n };\n\n useEffect(clearAnimationFrame);\n useEffect(() => clearAnimationFrame, []);\n\n return useCallback((immediately = false) => {\n if (immediately) {\n rerender();\n return;\n }\n\n if (animationFrameIdRef.current) {\n return;\n }\n\n animationFrameIdRef.current = requestAnimationFrame(rerender);\n }, []);\n}\n\nexport function useOnResize<T extends HTMLElement>(\n ref: RefObject<T> | undefined,\n onResize: () => void\n) {\n const onResizeRef = useRef(onResize);\n onResizeRef.current = onResize;\n\n const [observer] = useState(\n () =>\n new ResizeObserver(() => {\n onResizeRef.current();\n })\n );\n\n useEffect(() => {\n if (!ref?.current) {\n return;\n }\n\n const { current: element } = ref;\n observer.observe(element);\n return () => observer.unobserve(element);\n }, [observer, ref]);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ export declare type Store<T> = readonly [
2
+ (onStoreChange: () => void) => () => void,
3
+ () => T
4
+ ];
5
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oBAAY,KAAK,CAAC,CAAC,IAAI,SAAS;IAC9B,CAAC,aAAa,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI;IACzC,MAAM,CAAC;CACR,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { CursorEditor, CursorState } from '@getguru/slate-yjs-core';
2
+ import { BaseRange } from 'slate';
3
+ export declare function getCursorRange<TCursorData extends Record<string, unknown> = Record<string, unknown>>(editor: CursorEditor<TCursorData>, cursorState: CursorState<TCursorData>): BaseRange | null;
4
+ //# sourceMappingURL=getCursorRange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCursorRange.d.ts","sourceRoot":"","sources":["../../src/utils/getCursorRange.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,WAAW,EAEZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAqB,MAAM,OAAO,CAAC;AAOrD,wBAAgB,cAAc,CAC5B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAErE,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,EACjC,WAAW,EAAE,WAAW,CAAC,WAAW,CAAC,GACpC,SAAS,GAAG,IAAI,CA2BlB"}
@@ -0,0 +1,24 @@
1
+ import { BaseRange, Path, Text } from 'slate';
2
+ import { ReactEditor } from 'slate-react';
3
+ export declare type SelectionRect = {
4
+ width: number;
5
+ height: number;
6
+ top: number;
7
+ left: number;
8
+ };
9
+ export declare type CaretPosition = {
10
+ height: number;
11
+ top: number;
12
+ left: number;
13
+ };
14
+ export declare type OverlayPosition = {
15
+ caretPosition: CaretPosition | null;
16
+ selectionRects: SelectionRect[];
17
+ };
18
+ export declare type GetSelectionRectsOptions = {
19
+ xOffset: number;
20
+ yOffset: number;
21
+ shouldGenerateOverlay?: (node: Text, path: Path) => boolean;
22
+ };
23
+ export declare function getOverlayPosition(editor: ReactEditor, range: BaseRange, { yOffset, xOffset, shouldGenerateOverlay }: GetSelectionRectsOptions): OverlayPosition;
24
+ //# sourceMappingURL=getOverlayPosition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOverlayPosition.d.ts","sourceRoot":"","sources":["../../src/utils/getOverlayPosition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,IAAI,EAAS,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,oBAAY,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,oBAAY,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,oBAAY,eAAe,GAAG;IAC5B,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC,CAAC;AAEF,oBAAY,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;CAC7D,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,SAAS,EAChB,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,EAAE,wBAAwB,GACpE,eAAe,CA8EjB"}
@@ -0,0 +1,4 @@
1
+ import { ReactEditor } from 'slate-react';
2
+ import { BaseRange } from 'slate';
3
+ export declare function reactEditorToDomRangeSafe(editor: ReactEditor, range: BaseRange): Range | null;
4
+ //# sourceMappingURL=react-editor-to-dom-range-safe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-editor-to-dom-range-safe.d.ts","sourceRoot":"","sources":["../../src/utils/react-editor-to-dom-range-safe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,SAAS,GACf,KAAK,GAAG,IAAI,CAMd"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@getguru/slate-yjs-react",
3
+ "sideEffects": false,
4
+ "version": "1.1.1",
5
+ "description": "React specific components/utils for slate-yjs.",
6
+ "keywords": [
7
+ "slate",
8
+ "yjs",
9
+ "collaborative",
10
+ "react"
11
+ ],
12
+ "type": "module",
13
+ "exports": {
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "main": "dist/index.cjs",
18
+ "module": "dist/index.js",
19
+ "unpkg": "dist/index.global.js",
20
+ "types": "dist/index.d.ts",
21
+ "scripts": {
22
+ "build": "yarn run tsup && tsc --emitDeclarationOnly",
23
+ "dev": "yarn run tsup --watch"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/guruhq/slate-yjs.git"
28
+ },
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@getguru/slate-yjs-core": "^1.0.3",
32
+ "@types/react": "^17.0.34",
33
+ "@types/use-sync-external-store": "^0.0.3",
34
+ "react": "^17.0.2",
35
+ "slate": "^0.82.1",
36
+ "tsup": "^5.12.1",
37
+ "typescript": "^4.6.3",
38
+ "yjs": "^13.5.29"
39
+ },
40
+ "peerDependencies": {
41
+ "@getguru/slate-yjs-core": "^1.0.3",
42
+ "react": ">=16.8.0",
43
+ "slate": ">=0.70.0",
44
+ "slate-react": ">=0.70.0",
45
+ "yjs": "^13.5.29"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/guruhq/slate-yjs/issues"
49
+ },
50
+ "homepage": "https://github.com/guruhq/slate-yjs#readme",
51
+ "dependencies": {
52
+ "use-sync-external-store": "^1.2.0",
53
+ "y-protocols": "^1.0.5"
54
+ }
55
+ }